Semaphore源码详解
Semaphore简介
Semaphore就是我们常说的信号量,本质就是基于AQS的一个共享锁。对AQS不太了解的可以看我之前写的AQS源码解析的文章AQS源码详细分析,让你掌握AQS原理,独占锁、共享锁、Condition
Semaphore常常被用作限流器,通过共享锁对资源进行限制。
Semaphore结构
如上图所示,Semaphore实现了非公平锁和公平锁两个模式。
Semaphore示例
class Pool {
private static final int MAX_AVAILABLE = 100;
// 初始化一个信号量,设置为公平锁模式,总资源数为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();
}
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;
}
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;
}
}
以上代码的场景是池子中有100个资源,线程可以单独申请其中一个,当申请不到的时候会被挂起等待。
Semaphore源码解析
1、初始化
public Semaphore(int permits) {
//permits为同一时刻容纳的最大线程数
//默认调用了非公平锁
sync = new NonfairSync(permits);
}
//也可以在初始化方法中传入fair
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
2、acquire()
//申请锁,可响应中断
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())//如果线程被中断,抛出异常
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)//小于0代表申请失败
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
//将state-=acquires
int available = getState();
int remaining = available - acquires;
//如果remaining<0直接返回
//如果CAS成功,返回
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
//doAcquireSharedInterruptibly我们在AQS源码详解中,已经详细说过了,不在展开说了
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
//加入锁queue中
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
//如果在queue中的第二个节点,尝试申请锁
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
//申请锁成功后,就将node移出queue
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
//将线程挂起
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
3、release()
public void release() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {//如果释放锁成功
doReleaseShared();//唤醒其他等待线程来争夺锁
return true;
}
return false;
}
protected final boolean tryReleaseShared(int releases) {
//cas将state+=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;
}
}
//doReleaseShared()用来唤醒其他线程,我们在AQS源码详解中,已经详细说过了,不在展开说了
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;
}
}
Semaphore模式解析
跟ReentranLock一样,Semaphore也支持tryAcquire()、tryAcquire(long timeout,TimeUnit unit)、acquireUninterruptible(),这些方法的源码就不再解析了,跟ReentrantLock的处理方法一模一样,只不过ReentrantLock申请的独占锁,而Semaphore申请的是共享锁。感兴趣的可以看下我对ReentrantLock的源码分析ReentrantLock源码详解
,然后再自己去看Semaphore的源码进行理解
方法 | 详解 |
---|---|
tryAcquire() | 尝试获取锁,只获取一次,申请不到就拉倒,抢锁期间不响应中断 |
tryAcquire(long timeout,TimeUnit unit) | 在timeout个unit时间内,申请锁,申请不到就挂起,抢锁期间可响应中端 |
acquire() | 阻塞式申请锁,申请不到就挂起,抢锁期间可响应中断 |
acquireUninterruptibly | 阻塞式申请锁,申请不到就挂起,抢锁期间不响应中断,在抢锁成功后才响应中断 |
Semaphore整体代码思路还是比较清晰的,如果朋友们能掌握AQS的话,Semaphore更不在话下了,Semaphore就是根据AQS构建的。
如果朋友们对AQS还不太了解的话,可以看我之前写的AQS源码解析AQS源码详细分析,让你掌握AQS原理,独占锁、共享锁、Condition