前言
在AQS源码解析一中,我们说到了AQS对锁资源获取的过程进行了抽象,帮我们实现了线程如何被挂起、如何被唤醒等操作,分析了独占模式和共享模式底层是如何实现的,今天我们来看看Semaphore(信号量)底层是怎么控制资源获取和释放的,这样可以进一步加深我们对AQS的理解。
Semaphore
内部使用共享锁实现
有公平和非公平的两种实现
以许可证为共享资源控制使用共享资源的线程数
线程抢夺到指定个数的许可证后 可执行
用完然后释放掉 阻塞的线程会抢夺许可证 抢到许可证后可执行
先来看看Semaphore的重要构造
/**
* @param permits 请求获取的许可证数量
* @param fair true 公平 false 非公平
*/
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
可以看到,内部是有公平和非公平的两种实现方式的
非公平模式下的获取与释放
非公平模式下的获取许可证
/**
* 获取一个许可证 可中断
*/
public void acquire() throws InterruptedException {
//调用的是AQS中的模板方法
sync.acquireSharedInterruptibly(1);
}
/**
* 获取共享锁 可中断 ASQ中的方法
*/
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
//当前线程被标记为中断的 直接抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//尝试获取共享锁 子类实现小于0
if (tryAcquireShared(arg) < 0)
//获取共享锁 AQS实现
doAcquireSharedInterruptibly(arg);
}
可以看到AQS的模板方法,这里是可中断的
1.如果线程被标记中断的就自己抛出异常
2.调用子类的tryAcquireShared返回小于0 调用doAcquireSharedInterruptibly,否则就是获取成功了
来看看Semaphore非公平模式对tryAcquireShared方法的实现
非公平的情况下,谁来都先可以尝试获取一下。
doAcquireSharedInterruptibly,这在AQS中有详细说到,线程所在的节点前驱如果是头节点,会再调用Semaphore下的tryAcquireShared尝试获取共享锁,如果获取成功之后返回的可用许可证大于0的话,会doReleaseShared唤醒等待共享锁的线程,获取失败的话线程很有可能会被挂起,然后直到被唤醒。
非公平模式下的释放许可证
/**
* 释放一个许可证
*/
public void release() {
//调用的是AQS中的模板方法
sync.releaseShared(1);
}
/**
* 释放共享锁 AQS中的方法
*/
public final boolean releaseShared(int arg) {
//尝试释放共享锁成功 子类实现
if (tryReleaseShared(arg)) {
//唤醒后继阻塞节点
doReleaseShared();
return true;
}
return false;
}
1.非公平模式的tryReleaseShared如果释放成功,就会调用doReleaseShared唤醒等待共享锁的线程
来看看Semaphore非公平模式对tryReleaseShared方法的实现
CAS+自旋保证释放成功
公平模式下的获取与释放
公平模式下的获取
AQS部分都和非公平锁一样,不同点就是在于tryAcquireShared,获取资源的具体策略上的不同。
来看看Semaphore公平模式对tryAcquireShared方法的实现
与非公平对比可以发现,多了一步**hasQueuedPredecessors()**阻塞队列中已经有线程在排队了直接返回-1(也就是前面有人了,按先到先得公平的方式,已经轮不到你了) 获取失败的,其它与非公平都一样。
公平模式下的释放许可证
/**
* 尝试释放许可证
* @param releases 准备释放的许可证个数
*/
protected final boolean tryReleaseShared(int releases) {
for (;;) {//死循环
//获取当前可用的许可证数量
int current = getState();
int next = current + releases;
if (next < current)
throw new Error("Maximum permit count exceeded");
//cas设置许可证的数量 成功返回true
if (compareAndSetState(current, next))
return true;
}
}
与非公平释放许可的过程一样。
总结
Semaphore使用共享锁来控制资源的获取,从而控制共享资源的获取。内部使用公平与非公平两种方式,公平:效率高,但是可能会出现长时间等待的线程,非公平:先到先得,后来要排队,可以保证不会有线程长时间等待得不到执行。关于Semaphore完整的源码解析点击这里。