使用概述
Semaphore是用来控制同时访问共享资源的线程数量,有流量控制的作用。
Semaphore维护一批许可,线程通过调用acquire方法来获取许可,获取不到则会发生阻塞,获取到的线程执行完自身逻辑后需要调用release方法进行释放。
经典使用场景如下:线程从资源池中获取资源时需要先获取许可,释放资源时也需要释放许可,以便后续的线程可以获得许可。构造函数的第2个参数指定是否采用公平策略,公平策略侧重于保证每个线程不会发生饥饿,而非公平策略侧重于提升吞吐量。获取资源类的需求一般倾向于使用公平策略。
public class Pool {
private static final int MAX_AVAILABLE = 100;
private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
protected Object[] items = new Object[MAX_AVAILABLE];
protected boolean[] used = new boolean[MAX_AVAILABLE];
public Object getItem() throws InterruptedException {
available.acquire();
return getNextAvailableItem();
}
public void putItem(Object x) {
if (markAsUnused(x)) {
available.release();
}
}
protected synchronized Object getNextAvailableItem() {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (!used[i]) {
used[i] = true;
return items[i];
}
}
return null; // not reached
}
protected synchronized boolean markAsUnused(Object item) {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (item == items[i]) {
if (used[i]) {
used[i] = false;
return true;
} else
return false;
}
}
return false;
}
}
源码分析
(1) 数据结构
//同步器,AQS的子类
private final Sync sync;
Sync为Semaphore内部的抽象类,继承AQS,通过同步队列来实现线程的调度。
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
//permits其实就是AQS中的共享变量state
Sync(int permits) {
setState(permits);
}
final int getPermits() {
return getState();
}
//返回剩余许可数
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
//释放许可,即将state加上释放许可数
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;
}
}
final void reducePermits(int reductions) {
for (;;) {
int current = getState();
int next = current - reductions;
if (next > current) // underflow
throw new Error("Permit count underflow");
if (compareAndSetState(current, next))
return;
}
}
final int drainPermits() {
for (;;) {
int current = getState();
if (current == 0 || compareAndSetState(current, 0))
return current;
}
}
}
Semaphore内部的Sync有两种实现:
- 公平同步器:严格按照同步队列的顺序获取许可,保证不会出现线程饥饿。
- 非公平同步器:新的线程在调用acquire方法时,会立即尝试获取许可,获取不到才会进入同步队列,可能会发生线程饥饿的情况,但吞吐量较高,这也是默认的同步器。
/**
* NonFair version
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -2694183684443567898L;
NonfairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
/**
* Fair version
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = 2014338818796000944L;
FairSync(int permits) {
super(permits);
}
//公平策略仅多出判定当前线程是否是同步队列的可执行节点
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;
}
}
}
(2) 常用方法
构造函数
//默认非公平同步器策略
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
//通过fair参数指定同步器
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
acquire()
acquire()调用AQS的acquireSharedInterruptibly(int arg)方法,可响应中断。
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
//class: AbstractQueuedSynchronizer
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
tryAcquireShared(int acquires)会调用Semaohore内部的FairSync或者NonfairSync相应的实现,其返回剩余的许可数量,区别在于FairSync会判定当前线程是否是同步队列中的可执行结点,而NonfairSync不用。
如果剩余的许可数量不足,则会执行AQS的doAcquireSharedInterruptibly(int arg)方法,该方法主要将当前线程以共享结点的方式加入到同步队列的尾部,然后就是在同步队列中等待被唤醒。
//class: AbstractQueuedSynchronizer
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
//以共享结点加入队列尾部
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
//判断当前结点的前驱结点是否是头结点
//如果是头结点,则可以尝试获取许可,获取成功则将当前结点设置为头节点,并尝试唤醒后继结点
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
//当前结点还不是可执行结点,则在队列中寻找到合适的位置进行等待
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
tryAcquire()
tryAcquire()只会尝试获取一次,无论成败都立即返回,不会进入同步队列进行等待。
//nonfairTryAcquireShared主要通过CAS更新state的值,并返回剩余的许可数,
//许可数小于0则表示获取失败,大于等于0表示获取成功
public boolean tryAcquire() {
return sync.nonfairTryAcquireShared(1) >= 0;
}
tryAcquire(long timeout, TimeUnit unit)
tryAcquire(long timeout, TimeUnit unit)方法回立即尝试获取许可,获取失败会进入同步队列等待,只是如果超时会返回。
该方法调用AQS的tryAcquireSharedNanos(int arg, long nanosTimeout)方法,同样可响应中断
public boolean tryAcquire(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
//class: AbstractQueuedSynchronizer
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquireShared(arg) >= 0 ||
doAcquireSharedNanos(arg, nanosTimeout);
}
tryAcquireSharedNanos(int arg, long nanosTimeout)方法会先立即尝试获取许可,获取失败则调用doAcquireSharedNanos(arg, nanosTimeout)方法,该方法与上述分析的doAcquireSharedInterruptibly(int arg)方法类似,只是多了一步超时直接返回的逻辑。
private boolean doAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return true;
}
}
nanosTimeout = deadline - System.nanoTime();
//如果超时则立即返回
if (nanosTimeout <= 0L)
return false;
//spinForTimeoutThreshold为自旋时间,默认为1000纳秒
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
release()
release()的作用是释放1个许可。
public void release() {
sync.releaseShared(1);
}
//class: AbstractQueuedSynchronizer
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
tryReleaseShared(int releases)调用Semaphore的内部类Sync的实现,逻辑其实很简单,就是把releases加回到共享变量state。释放许可后,调用AQS的doReleaseShared()方法唤醒后继结点。
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;
}
}