Semaphore也是高并发中控制并发量的工具,它维护一系列的运行许可。每个线程运行之前来获取许可,许可的数量有限,只有获得的线程才能继续运行,否则阻塞等待。
通过前面对CountDownLatch的学习,现在来理解Semaphore应该会轻松点。
一、基本代码结构
public class Semaphore implements java.io.Serializable {
/** Semaphore的核心逻辑在Sync */
private final Sync sync;
/** Sync继承于AQS */
abstract static class Sync extends AbstractQueuedSynchronizer {
Sync(int permits) {
setState(permits);
}
final int getPermits() {
return getState();
}
}
/** 构造器默认使用非公平模式 */
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
/** 指定是否使用公平模式 */
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
}
二、获取许可
获取许可的方法是acquire()
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
/** 如果返回小于0,那么调用doAcquireSharedInterruptibly(arg)方法,阻塞线程等待,
另外tryAcquireShared有公平和非公平之分,后面代码分析 */
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
/** 非公平模式获取许可 */
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
/** 非公平获取许可,直接用CAS竞争 */
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
/** 公平模式获取许可,如果等待队列中有线程等待获取许可,那么返回-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;
}
}
三、释放许可
使用Semaphore使用完许可之后必须释放许可,否则后面的线程将会得不到许可而不能运行。释放许可的方法是release(),我们下面分析
public void release() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
// tryReleaseShared(arg)方法在Semaphore类中实现,我们后面讲解
if (tryReleaseShared(arg)) {
// doReleaseShared()方法是AQS的方法,主要是唤醒等待线程去获取许可
doReleaseShared();
return true;
}
return false;
}
/** 释放许可,代码逻辑是CAS相加释放的许可数量 */
protected final boolean tryReleaseShared(int releases) {
// 轮询直到成功
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;
}
}
四、总结
从上面分析中,我们可以得出Semaphore的几个结论:
- 使用许可的数量控制并发数量,内部使用AQS维护等待许可的线程,使用CAS竞争获取许可。
- 公平非公平之分:公平模式的Semaphore可以让等待许可的线程先到先得。
- 有借有还。许可使用完之后必须归还。