前言
Semaphore(信号量)是一种用于多线程编程的同步工具,用于控制同时访问某个资源的线程数量。
Semaphore维护了一个计数器,线程可以通过调用acquire()方法来获取Semaphore中的许可证,当计数器为0时,调用acquire()的线程将被阻塞,直到有其他线程释放许可证;线程可以通过调用release()方法来释放Semaphore中的许可证,这会使Semaphore中的计数器增加,从而允许更多的线程访问共享资源。
常用API
构造器
/**
* Creates a {@code Semaphore} with the given number of
* permits and nonfair fairness setting.
*
* @param permits the initial number of permits available.
* This value may be negative, in which case releases
* must occur before any acquires will be granted.
*/
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
/**
* Creates a {@code Semaphore} with the given number of
* permits and the given fairness setting.
*
* @param permits the initial number of permits available.
* This value may be negative, in which case releases
* must occur before any acquires will be granted.
* @param fair {@code true} if this semaphore will guarantee
* first-in first-out granting of permits under contention,
* else {@code false}
*/
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
- permits 表示许可证的数量(资源数)
- fair 表示公平性,如果这个设为 true 的话,下次执行的线程会是等待最久的线程
常用方法
- acquire() 表示阻塞并获取许可
- tryAcquire() 方法在没有许可的情况下会立即返回 false,要获取许可的线程不会阻塞
- release() 表示释放许可
Semaphore使用
1.Semaphore实现服务接口限流
代码如下(示例):
public class SemaphoreDemo {
private static Semaphore semaphore = new Semaphore(2);
private static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
for(int i = 0;i<10;i++){
executor.submit(()->getProductInfo());
}
executor.shutdown();
}
public static String getProductInfo(){
try {
semaphore.acquire();
System.out.println("请求服务");
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
semaphore.release();
}
System.out.println(Thread.currentThread().getName()+"释放资源");
return "商品详情信息";
}
public static String getProductInfo2(){
if(!semaphore.tryAcquire()){
System.out.println("请求被流控了");
return "请求被流控了";
}
try {
System.out.println("请求服务");
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
semaphore.release();
}
System.out.println(Thread.currentThread().getName()+"释放资源");
return "商品详情信息";
}
}
/**
* getProductInfo:
* 请求服务
* 请求服务
* 请求服务
* 请求服务
* pool-1-thread-4释放资源
* pool-1-thread-5释放资源
* 请求服务
* pool-1-thread-2释放资源
* pool-1-thread-8释放资源
* 请求服务
* 请求服务
* pool-1-thread-3释放资源
* 请求服务
* pool-1-thread-7释放资源
* pool-1-thread-1释放资源
* 请求服务
* 请求服务
* pool-1-thread-6释放资源
* pool-1-thread-10释放资源
* pool-1-thread-9释放资源
*
*
*
* getProductInfo2:
*
* 请求服务
* 请求被流控了
* 请求被流控了
* 请求被流控了
* 请求被流控了
* 请求服务
* 请求被流控了
* 请求被流控了
* 请求被流控了
* 请求被流控了
* pool-1-thread-4释放资源
* pool-1-thread-2释放资源
*/
1.Semaphore实现数据库连接池
代码如下(示例):
public class SemaphoreDemo2 {
private static ConnectPool pool = new ConnectPool(2);
private static ExecutorService executor = Executors.newCachedThreadPool();
public static void main(String[] args) {
for(int i = 0;i<5;i++){
final int id = i + 1;
executor.execute(()->{
Connect connect = null;
try {
System.out.println("线程" + id + "等待获取数据库连接");
connect = pool.openConnect();
System.out.println("线程" + id + "已拿到数据库连接:" + connect);
Thread.sleep(2000);
System.out.println("线程" + id + "释放数据库连接:" + connect);
}catch (InterruptedException e){
e.printStackTrace();
}finally {
pool.releaseConnect(connect);
}
});
}
}
}
class ConnectPool{
private int size;
private Connect[] connects;
private boolean[] connectFlag;
private Semaphore semaphore;
public ConnectPool(int size){
this.size = size;
semaphore = new Semaphore(size,true);
connects = new Connect[size];
connectFlag = new boolean[size];
initConnects();
}
private void initConnects(){
for(int i = 0;i<this.size;i++){
connects[i] = new Connect();
}
}
public Connect openConnect() throws InterruptedException {
semaphore.acquire();
return getConnect();
}
private synchronized Connect getConnect(){
for(int i = 0;i<connectFlag.length;i++){
if(!connectFlag[i]){
connectFlag[i] = true;
return connects[i];
}
}
return null;
}
public synchronized void releaseConnect(Connect connect){
for(int i = 0;i<this.size;i++){
if(connect == connects[i]){
connectFlag[i] = false;
semaphore.release();
}
}
}
}
/**
* 数据库连接
*/
class Connect{
private static int count = 1;
private int id = count++;
public Connect(){
//假设打开一个连接需要1s
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("连接#"+id+"#");
}
@Override
public String toString(){
return "#"+id+"#";
}
}
/**
* 连接#1#
* 连接#2#
* 线程3等待获取数据库连接
* 线程1等待获取数据库连接
* 线程2等待获取数据库连接
* 线程4等待获取数据库连接
* 线程5等待获取数据库连接
* 线程1已拿到数据库连接:#2#
* 线程3已拿到数据库连接:#1#
* 线程1释放数据库连接:#2#
* 线程2已拿到数据库连接:#2#
* 线程3释放数据库连接:#1#
* 线程4已拿到数据库连接:#1#
* 线程2释放数据库连接:#2#
* 线程5已拿到数据库连接:#2#
* 线程4释放数据库连接:#1#
* 线程5释放数据库连接:#2#
*/
应用场景总结
以下是一些使用Semaphore的常见场景:
- 限流:Semaphore可以用于限制对共享资源的并发访问数量,以控制系统的流量。
- 资源池:Semaphore可以用于实现资源池,以维护一组有限的共享资源。