前言
Semaphore意为信号量,它用来限制某段时间内的最大并发资源数。例如数据库连接池,停车位等。下面通过停车位的栗子来说明Semaphore的使用方式。
import java.util.concurrent.Semaphore;
public class SemaphoreDemo {
private static Semaphore semaphore = new Semaphore(10);
public static void main(String[] args) {
for (int i = 1; i <= 50; i++) {
new Thread(() -> {
try {
if (semaphore.availablePermits() == 0) {
System.out.println("车位已满,需要等待...");
}
semaphore.acquire();
System.out.println("有空余车位,驶进停车场");
// 模拟在停车场smoke or something
Thread.sleep(3000);
System.out.println("老婆喊我回家吃饭,驶出停车场");
semaphore.release();
} catch (InterruptedException e) {
// ignored
}
}).start();
}
}
}
实现原理
看一眼Semaphore的类结构,内部类继承了AQS,同时提供了公平和非公平策略。
我们可以在构造函数中指定是公平还是非公平,默认是非公平策略。
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
再来看重要方法(以NonfairSync为例分析):
acquire()
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
可以看到,调用了AQS的模板方法,acquireSharedInterruptibly里面会调用子类重写的tryAcquireShared,来看看相关逻辑:
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 调用子类方法尝试获取共享资源失败,则在队列中阻塞获取
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
final int nonfairTryAcquireShared(int acquires) {
// CAS + 自璇
for (;;) {
// 获取当前剩余资源数
int available = getState();
// 计算获取acquires个资源后,剩余资源数
int remaining = available - acquires;
// 如果不够用或者够用并且CAS设置剩余数成功,则返回
// 否则循环重试CAS操作
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
release()
public void release() {
sync.releaseShared(1);
}
同样,release调用了AQS的模板方法,releaseShared里面会调用子类重写的tryReleaseShared方法,来看看子类具体实现逻辑:
protected final boolean tryReleaseShared(int releases) {
// CAS + 自璇
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
代码逻辑也很简单,不做赘述。
FairSync公平式的获取,就是在tryAcquireShared时先判断队列中有无在等待的元素,有的话就返回-1,进入同步队列阻塞获取。相关代码如下:
protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
参考资料:
《Java并发编程之美》