AQS 是 JUC 的基石,提供了数据结构和底层实现方法,比如获取锁的方式由子类实现完成出入队、唤醒线程由功能。这里只分析 AQS 已经实现了的功能逻辑,如果要分析完成的功能需要配合具体的子类比如 ReentrantLock
核心思想
如果共享资源空闲,当前线程就工作,并锁住资源。如果共享资源被占用当前线程就进入队列,并等到条件合适及时唤醒
资源占用方式
独占:又分为公平(严格排队,先进先出)和非公平(插队,插不成功就排队,就是公平锁了),比如 ReentrantLock
共享:实现的有 Semaphore/CountDownLatch。Semaphore、CountDownLatCh、 CyclicBarrier、ReadWriteLock
内部重要属性
private transient volatile Node head; // 队头
private transient volatile Node tail; // 队尾
private volatile int state; // 共享资源状态
static final class Node; // 内部类 Node
public class ConditionObject; // 内部类 ConditionObject
-
head 和 tail:对头与队尾,队头不是同步队列中的第一个节点(第一个线程是正在工作的线程,没理由放入等待队列)
-
state:共享资源状态,同步状态,资源被占用次数
- 0 共享资源未被使用,1 表示共享资源已被使用,大于1表示重入锁(同一个线程多次访问共享资源)
- 为什么需要这个值?不然直接读内存吗?每个线程都会缓存这个值,一个线程更新,别的线程可见(volatile保证)
- 比如 reentrantlock.tryLock() 初次获取锁成功 cas 更新的时候,如果是 0 才更新为1,不然就失败
-
内部类 Node:同步队列中的节点,是由线程封装出来的,每个节点有两个指针分别指向前一个和后一个
static final Node SHARED = new Node();// 占用资源方式:共享 static final Node EXCLUSIVE = null;// 占用资源方式:独占 volatile int waitStatus;// 当前节点状态 volatile Node prev;// 前驱指针,指向前一个节点 volatile Node next;// 后驱指针 volatile Thread thread;// 结点所对应的线程 Node nextWaiter;// 也是一个指针,指向下一个处于 CONDITION 状态的节点
waitStatus 当前节点的状态,不要和 aqs 中的 state 混淆了,aqs 的 state 是共享资源的状态,waitStatus 是每个节点的状态,取值范围如下:
状态 值 含义 CALCELLED 1 获取锁的线程被取消了 CONDITION -2 表示节点在等待队列中,线程需要等待唤醒 PROPAGATE -3 共享锁模式下才会使用,独占锁不会用 SIGNAL -1 线程已经准备好了,就等资源释放
入队过程
-
lock() 方法作为入口,tryAcquire 方法获取锁,如果获取成功,就没后续步骤了,也不会维护队列,节点状态等
- tryAcquire cas 算法更改 aqs 的 state
- 由子类实现,比如 ReentrantLock 的公平和非公平
-
如果 tryAcquire 获取锁失败,就要维护队列了,aqs 的 addWaiter 方法已经实现好了的,逻辑如下
- 如果此时队列为空,执行 enq 方法创建一个节点,不初始化任何属性(虚拟节点),作为队头(aqs 的 head)
- 如果队列不为空,不会执行 enq,只是维护链表,当前节点作为队尾,并维护指向关系
-
入队成功后执行 acquireQueued,两个作用:更改前一个节点状态和挂起当前节点的线程
出队过程
- 都是由 unLock() 方法作为入口
- 更改 aqs 的 head 和 tail 指向
- 涉及链表操作,顺便把下一个节点唤醒