上一期介绍了CyclicBarrier的源码解析,今天来聊一聊最后一个工具:信号量 Semaphore。Semaphore用来控制同时操作某个资源的操作数量。Semaphore管理着permits,每当一个线程来获取许可时,permits数减1,当permits数小于0时,再来获取许可的资源就需要阻塞。下面来详细看看Semaphore的实现原理。
内部类
Semaphore支持公平和非公平两种实现。
//非公平版本
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -2694183684443567898L;
//permits值最终赋值给了AQS的state值
NonfairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
ret urn nonfairTryAcquireShared(acquires);
}
}
//公平版本
static final class FairSync extends Sync {
private static final long serialVersionUID = 2014338818796000944L;
FairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
for (;;) {
//可以看出,公平版本的acquire需要先去判断前面是否有排队的
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
}
构造方法
permits表示许可的数量,需要在创建Semaphore的时候赋值,Semaphore默认为非公平的。
如果要使用公平的,则需要指定fair参数为true。
public c(int permits) {
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
方法
acquire
acquire方法调用了Sync的acquireSharedInterruptibly方法
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
tryAcquireShared方法在Semaphore中分为公平和非公平的两种方式,公平的方式会先去判断前面有没有排队获取许可的线程,如果有说明许可是小于0的,所以直接返回-1。之后公平和非公平的操作是相同的,都是获取state值,减1,通过cas的方式更新state值,返回更新后的值。
tryAcquireShared返回的值如果小于0,说明许可值不够了,需要去排队等待。
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
//把SHARED的node加到队列尾部
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
//如果node的prev是head,则去尝试获取许可
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
//获取许可成功,把node设置为head,跳出循环
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
//node的prev不是head的话,则挂起线程
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
release
release方法释放一个许可,把它归还给Semaphore,然后把可用的许可(permits)数目加1。
release额方法调用了AQS的releaseShared方法
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
tryReleaseShared方法通过cas的方式把当前的state值更新为state+arg。
private void doReleaseShared() {
for (;;) {
Node h = head;
//如果等待队列不为空的话,则去唤醒下一个节点
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
doReleaseShared的操作在CountDownLatch的countDown方法中也有用到,大家可以去参照countDown方法。
示例
Semaphore的源码中给出了使用的实例,大家可以参考,毕竟官方的肯定是很有指导意义的。
class Pool {
private static final int MAX_AVAILABLE = 100;
private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
public Object getItem() throws InterruptedException {
available.acquire();
return getNextAvailableItem();
}
public void putItem(Object x) {
if (markAsUnused(x))
available.release();
}
// Not a particularly efficient data structure; just for demo
protected Object[] items = ... whatever kinds of items being managed
protected boolean[] used = new boolean[MAX_AVAILABLE];
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;
}
}