1 简介
AbstractQueuedSynchronizer抽象的队列式同步器(抽象类)。提供了一个FIFO队列,可以看成是一个用来实现同步锁以及其他涉及到同步功能的核心组件,常见的有:ReentrantLock、CountDownLatch等。
其中比较重要的概念有:
state共享资源
FIFO
CAS
park unpark
2 AQS线程队列
Node是整个队列中最核心的部分,包含CANCELLED、SIGNAL、CONDITION、PROPAGATE四种状态、指向前后节点的指针、thread是节点存储的值。
ReentrantLock中的公平锁与非公平锁都继承了这个抽象类。
ReentrantLock包含公平锁和非公平锁,每种锁里面还包含了抽象的锁Sync,抽象锁执行AQS。
如果当前是线程1拥有锁的话,线程2,3,4会调用park操作来被挂起加入队列中。
当拥有锁的Thread1执行完毕后会调用unpark方法,head指向下一个节点。由于ReentrantLock是一个可重入锁,重入次数被AQS.state记录,每重入一次值 + 1,退出一次 - 1。
3 ReentrantLock实例构建
ReentrantLock默认构造函数创建的是非公平锁,如果想要公平锁的话需要在构造方法中传入true。
4 多线程抢锁图示(非公平锁)
线程抢到锁的过程使用CAS实现。
初始时等待队列只有一个Head节点,其中存储的Thread是null,用来占位,线程B,C在队列中使用双向链表的方式与Head相连,调用park挂起。
上图的分步过程见下文。
4.1 A线程加锁
A线程抢锁时通过CAS操作将state改为1,并将AQS的exclusiveOwnerThread属性设为A,表示当前锁的拥有者。
4.2 B线程加锁
此时B线程要进行抢锁,发现state已经是1了,所以CAS操作失败,进入到队列中,waitStatus设置为0
第一次new Node的时候,即创建head,thread设置为了null,waitStatus设置为SIGNAL
4.3 C线程加锁
接着C线程抢锁时,操作同B,会将B的waitStatus设置为SIGNAL,自己的是初始化是的值0
新插入的节点waitStatus值都设为0原因是用来标记队列中最后一个节点,此时不必再进行对后续节点的unpark操作,等待队列中没有节点时会有一个null的节点用作占位,这两个操作都是防止等待队列中没有等待的线程时而变为空。
4.4 A线程解锁
A线程进行解锁操作之后,B线程可以抢到锁,流程如下:
A线程解锁操作:
state设为0
exclusiveOwnerThread设为null表示锁被释放
head节点向后移动一位进行unpark操作唤醒线程B
之前的head节点要被释放
4.5 B线程解锁
4.6 C线程解锁