-
AbstractQueuedSynchronizer是什么
是用来构建锁或者其他同步器组件的重量级基础框架及整个JUC体系的基石,通过内置的FIFO队列来完成资源获取线程的排队工作,使用int类型变量表示持有锁的状态。(抽象的队列同步器)
-
谁与AbstractQueuedSynchronizer有关
CountDownLatch
ReentrantLock
Semaphore
以上只是截取一小部分。
-
CLH队列
-
AbstractQueuedSynchronizer有什么用
如果共享资源被占用,就需要一定的阻塞等待唤醒机制来保证锁的分配。这个机制主要用的是CLH队形实现的,将暂时获取不到锁的线程加入到队列种中,这个队列就是AQS的抽象表现。它将请求共享资源的线程封装成队列的结点(Node),通过CAS、自旋以及LockSupport.park()的方式,维护state变量的状态,使并发达到同步的效果。
AQS使用一个volatile的int类型的成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取的排队工作,将要抢占资源的线程封装成一个Node节点来实现锁的分配,通过CAS完成对state值的修改。
-
AbstractQueuedSynchronizer中的Node(JDK1.8)
shared:表示线程以共享的模式等待锁。
exclusive:表示线程正在以独占的方式等待锁。
cancelled:为1,表示线程获取锁的请求取消。
signal:为-1,表示线程已经准备好,等资源释放。
condition:为-2,表示节点在等待队列中,节点线程等待唤醒。
propagate:为-3,当前线程处在shared情况下,该字段才会使用。
waitStatus:当前节点在队列中的状态。
prev:前驱指针。
next:后继指针。
thread:当前节点的线程。
nextWaiter:指向下一个处于condition状态的节点。
-
AbstractQueuedSynchronizer的源码(JDK1.8)
AQS=state+CLH队列 、 Node=waitStatus+Thread
下面以ReentrantLock为例。
public class ReentrantLockDemo {
public static void main(String[] args) {
ReentrantLock reentrantLock = new ReentrantLock();
//线程A
new Thread(()->{
reentrantLock.lock();
try {
System.out.println("A");
try {
TimeUnit.MILLISECONDS.sleep(50);
}catch (Exception e){
e.printStackTrace();
}
}finally {
reentrantLock.unlock();
}
},"A").start();
//线程B
new Thread(()->{
reentrantLock.lock();
try {
System.out.println("B");
try {
TimeUnit.MILLISECONDS.sleep(5);
}catch (Exception e){
e.printStackTrace();
}
}finally {
reentrantLock.unlock();
}
},"B").start();
//线程C
new Thread(()->{
reentrantLock.lock();
try {
System.out.println("C");
}finally {
reentrantLock.unlock();
}
},"C").start();
}
}
reentrantLock.lock()
reentrantLock.lock()会调用Sync类中的lock()
lock()为模板模式,NonfairSync、FairSync类分别实现lock() 。
以NonfairSync为例,第一步先抢锁通过CAS来判断是否可以将state=0更新为state=1。
如果抢到锁就进入第2步将exclusiveOwnerThread设置为当前线程,否则进入acquire(int arg)。
进入tryAcquire(arg),tryAcquire(int arg)为模板模式,在NonfairSync中定义如下图。
当线程申请不到锁时,进入addWrite(Node.EXCLUSIVE)此时pred为NULL所以进入enq(node).
enq(node)使用for(;;)就是CAS初始化节点。
返回Node节点进入acquireQueued(addWaiter(Node.EXCLUSIVE), arg))。
进入shouldParkAfterFailedAcquire(p, node)将哨兵节点的waitStatus更新为-1,返回false。
第二次进入shouldParkAfterFailedAcquire(p, node)返回true,进入parkAndCheckInterrupt(),B线程挂起。
C线程进来和B线程流程一样调用addWrite(Node.EXCLUSIVE)初始化节点。
将B的waitStatus更新为-1,继续自旋,将线程C挂起。
reentrantLock.unlock()
调用sync.release(1)。
进入tryRelease(arg)更新state值和exclusiveOwnerThread为null。
返回true进入unparkSuccessor(h),释放A线程占有的锁。
此时B线程进入红框内(此时可以有线程D抢到锁,导致B继续自旋),如果B抢到锁就将前驱节点的引用置为NULL,方便GC。
C线程依次类推。