2020年6月24日
AQS的初步理解
AQS-抽象队列式同步器,常用的同步类,ReentrantLock、CountDownLatch、Semaphore、CyclicBarrier等都用到AQS
由一个volatile的state,和一个FIFO的队列组成
这里volatile
能够保证多线程下的可见性,当state=1
则代表当前对象锁已经被占有,其他线程来加锁时则会失败,加锁失败的线程会被放入一个FIFO
的双向等待队列中,比列会被UNSAFE.park()
操作挂起,等待其他获取锁的线程释放锁才能够被唤醒。
state状态修改是通过cas实现的。
下图可以简单概括

非公平的设置下,当线程竞争的时候,首先修改state为1的线程获得锁,并把head指向该线程,setExclusiveOwnerThread为当前线程,在可重入的情况下,每次重入state+1。锁释放的时候,会唤醒head节点的后续线程,开始竞争锁,成功后会把原来的线程node节点从head和后续节点断开,等待垃圾回收。以上是没有其他新的竞争的情况,如果等待节点被唤醒,同时也有新的线程加入竞争,那么此时获得锁的不一定是排在前面的节点。这是非公平锁的实现。
公平锁的设置下,如果队列中有元素等待,那么新的线程直接进入等待队列的尾部,具体的区别就在与tryAcquire()方法加了判断
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
如果hasQueuedPredecessors()返回false,表示没有等待线程,那么才参与竞争。
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&((s = h.next) == null || s.thread != Thread.currentThread());
}
之前写过一篇,这次加了点图,方便理解。
https://www.zeroheart.xyz/wordpress/?p=283
更多的细节,可以参考一下这篇
https://mp.weixin.qq.com/s/trsjgUFRrz40Simq2VKxTA
AQS中的等待唤醒,是用locksupport来实现的,park-unpark替代了obj的wait-notify机制
传统的wait-notify和lock的await,signal都需要 1.获取锁,才能调用,2.都需要先wait才能唤醒,而locksupport不同可以不用在lock块中执行,也可以先唤醒在等待。

调用unpark就会给permit+1=1,调用park就会消费一次1->0,立刻返回,如果原来就是0,那就阻塞等待unpark






