AQS系列之AbstractQueuedSynchronizer基础分析
AQS系列之以排斥锁分析
AQS系列之共享锁解析
AQS系列之共享锁的应用解析:Semaphore
AQS系列之共享锁的应用解析:Semaphore
- 一、简介
- 1. Sync
- 2. state
- 3. 对外方法
- 二、资源获取acquire
- 1. 非公平锁
- 1. 公平锁
- 三、资源释放
一、简介
Semaphore即信号量,常用于同时限制访问某些资源的线程数量。
其内部抽象类Fair继承了AQS,Semaphore正是通过Sync实现数量的控制
Semaphore是基于AQS原理实现的,但并不是说Semaphore继承了AbstractQueuedSynchronizer抽象类,而是其内部类进行了AbstractQueuedSynchronizer的继承,Semaphore通过内部类实现,后续其他几个AQS的应用同样也是如此。
具体到Semaphore,内部类Sync继承了AQS,而且Sync同样的也只是个抽象类,具体有氛围两个策略:公平锁(FairSync)和非公平锁(UnFairSync),两者的区别是对于竞争资源的线程是否严格遵守先到先得的公平策略。
Semaphore默认使用非公平锁。
Sync的state代表可以同时访问的线程数量,也可能理解为访问的许可证(permit)数量。每个线程访问(acquire)时需要拿到对应的许可证,否则进行阻塞,访问结束则返还(release)许可证。
state只能在Semaphore的构造方法中进行初始化,后续不能进行修改。
上图整理了Semaphore方法,主要是了解其思路,不是细究提供了哪些api。AQS其他应用类的一些方法思路也差不多,虽然命名可以有一定差异。后续在对其他类解析时不会再整理一次。
根据前面几篇文章对AQS的解析,共享锁不同应用类的核心逻辑实在其对AbstractQueuedSynchronizer的protected int tryAcquireShared(int acquires)的重写上。
state代表剩余的permit数量,只要state大于acquire则说明资源满足本线程所需,获取成功,否则失败。
public boolean tryAcquire(int permits) {
if (permits < 0) throw new IllegalArgumentException();
return sync.nonfairTryAcquireShared(permits) >= 0;
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
非公平锁的逻辑很简单,如果剩余remaining 小于 acquires则说明资源不足,返回失败,否则cas更新,cas失败则重试。
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;
}
}
可以看到相比非公平锁基本只多了hasQueuedPredecessors的判断
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
这个方法是定义在父类中的而且是final,也并不复杂,遍历队列,从头结点开始检查,到当前线程的节点截止(没有当前线程节点则到尾结点截止),是否存在其他节点。即当前线程节点是否存在前驱节点。
这一点用于满足按照队列顺序分配资源,即公平锁的含义,先到先得。
根据前面几篇文章对AQS的解析,共享锁不同应用类的核心逻辑实在其对AbstractQueuedSynchronizer的protected boolean tryReleaseShared(int arg)的重写上。
state代表剩余的permit数量,那么释放资源就是将线程占据的permit换到state上。
在Semaphore中,release方法有Sync重写,公平锁和非公平锁没有区别
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;
}
}
可以看到逻辑很简单,通过cas+重试的方式增加state。



