AbstractQueuedSynchronizer,抽象的队列同步器,位于rt.jar包中的java.util.concurrent.locks目录下,总的来说可以说AQS属于锁的分配机制。
CLH:Craig、Landin and Hagersten 队列,是一个单向链表,AQS中的队列CLH是变体的虚拟双向队列FIFO
如果共享资源被占用,就需要一定的阻塞等待唤醒机制来保证锁分配。这个机制主要用的是CLH队列的变体实现的,将暂时获取不到锁的线程加入到队列中,这个队列就是AQS同步队列的抽象表现。它将要请求共享资源的线程及自身的等待状态封装成队列的结点对象(Node),通过CAS、自旋以及LockSupport.park()的方式,维护state变量的状态,使并发达到同步的效果。
简单看看源码:
AQS使用一个volatile的int类型的成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取的排队工作将每条要去抢占资源的线程封装成一个Node节点来实现锁的分配,通过CAS完成对State值的修改
/**
* The synchronization state.
*/
private volatile int state;
/**
* Head of the wait queue, lazily initialized. Except for
* initialization, it is modified only via method setHead. Note:
* If head exists, its waitStatus is guaranteed not to be
* CANCELLED.
*/
private transient volatile Node head;
/**
* Tail of the wait queue, lazily initialized. Modified only via
* method enq to add new wait node.
*/
private transient volatile Node tail;
双向队列,从尾部入队,从头部出队
Node的等待状态waitStatus成员变量,源码:
/** 共享模式 */
static final Node SHARED = new Node();
/** 独占模式 */
static final Node EXCLUSIVE = null;
/** 线程被取消了 */
static final int CANCELLED = 1;
/** 后继线程需要唤醒 */
static final int SIGNAL = -1;
/** 等待condition唤醒 */
static final int CONDITION = -2;
/**
* 共享式同步状态获取将会无条件地传播下去
*/
static final int PROPAGATE = -3;
/** 初始为0,状态是上面的几种 */
volatile int waitStatus;
/** 前置节点 */
volatile Node prev;
/** 后继节点 */
volatile Node next;
/** 表示处于该节点的线程 */
volatile Thread thread;
AQS的使用,基本都是通过被【聚合】一个【队列同步器】的子类完成线程访问控制的;ReentrantLock,Semaphore,CountDownLatch等均是如此
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
非公平锁通过CAS抢到锁时会立即更新并占用,另外FairSync和NonfairSync获取锁时的区别:
对比公平锁和非公平锁的 tryAcquire()方法的实现代码,其实差别就在于非公平锁获取锁时比公平锁中少了一个判断 hasQueuedPredecessors()
hasQueuedPredecessors() 中判断了是否需要排队,导致公平锁和非公平锁的差异如下:
lock()方法中均调用了AQS的acquire()方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
以非公平锁为例,图中分析将以A,B,C三个顾客线程为例占用访问窗口state
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {// 资源空闲,则继续尝试抢锁
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {// 重入state+1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
// 未抢到锁,返回false
return false;
}
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
// 准备继续调用parkAndCheckInterrupt方法
if (ws == Node.SIGNAL)
return true;
// ws大于0说明是CANCELLED状态
if (ws > 0) {
// 循环判断前驱节点是否也为CANCELLED状态,忽略对应节点,重新连接队列
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
// 将当前节点的前驱节点设置为SIGNAL状态,用于后续唤醒操作
// 程序第一次执行到这返回false,还会进行外层第二次循环,最终调用该方法返回true
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
private final boolean parkAndCheckInterrupt() {
// 线程挂起,程序不会继续往下执行
LockSupport.park(this);
// 根据park()方法API描述,程序存在下列三种情况会继续往下执行
// 1. 被unpark
// 2. 被中断(interrupt)
// 3. 其他不合理逻辑的返回才会继续往下执行
// 因上诉三种情况程序执行至此,返回当前线程的中断状态,并清空中断状态
// 如果由于被中断,该方法会返回true
return Thread.interrupted();
}
线程A执行完程序,调用unlock()解锁,将state设置为1,调用LockSupport.unpark解锁,节点B继续执行,抢夺锁,将state改为1;head指针指向节点B,并清空B
public void unlock() {
sync.release(1);
}
// sync
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
// sync
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)// 解锁后,线程b不再阻塞
LockSupport.unpark(s.thread);
}
// 1. ReentrantLock.lock()
public void lock() {
sync.lock();// 默认非公平锁
}
// 2. NonfairSync.lock()
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
// 3. AbstractQueuedSynchronizer.acquire()
// 3.1 selfInterrupt(),如果执行了该方法说明原来的线程的中断标识位即true,故会再次将中断标识位置为true
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// 4. NonfairSync.tryAcquire()
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
// 5. Sync.nonfairTryAcquire()
// 5.1 c==0表示资源未被占用,尝试CAS抢锁,成功则返回true
// 5.2 current == getExclusiveOwnerThread()表示重入锁,state += 1,并返回true
// 5.3 返回true,表示当前线程持有锁,可以继续执行;返回false,表示当前线程抢锁失败,在3中继续执行后续条件方法
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
// 6. AbstractQueuedSynchronizer.addWaiter(),继3中未抢到锁后执行的方法
// 6.1 pred != null表示队列已经初始化,则将当前Node入队尾部
// 6.2 enq(node):方法中,会将当前node插入队尾,若未初始化队列,则将先初始化队列,再插入队尾
// 6.3 返回当前node节点,回到3中将当前node当参数传给acquireQueued()方法继续调用,后续流程看8
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
// 7. AbstractQueuedSynchronizer.enq()
// 7.1 t == null表示队列未初始化,则初始化虚拟节点为队头,并让队尾一起指向队头
// 7.2 自旋获取最新队尾,直到CAS成功将当前节点插入队尾,然后返回当前节点node
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
// 8. AbstractQueuedSynchronizer.acquireQueued(),由3中流程调用,返回后流程可继续看3
// 8.1 Node p = node.predecessor(),获取到的p为当前节点node的前驱节点
// 8.2 p == head && tryAcquire(arg),前驱节点为头节点则继续尝试获取锁;得到锁则将当前节点置为头节点,并将数据清空,丢弃原头节点,返回interrupted
// 8.3 shouldParkAfterFailedAcquire(p, node) true:表示当前线程应阻塞;false表示当前线程暂时不需要阻塞。false将循环再尝试获取锁,还未获取到才阻塞
// 8.4 parkAndCheckInterrupt() 使用LockSupport.park阻塞当前线程,获得许可后继续执行,返回当前线程的中断标志位并清除当前线程的中断标识位
// 8.5 failed,cancelAcquire() 发生异常,导致执行该块代码,将会将node从等待队列里移除
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
// 9.AbstractQueuedSynchronizer.shouldParkAfterFailedAcquire()
// 9.1 ws == Node.SIGNAL,表示pred的后继线程需要阻塞
// 9.2 ws > 0,表示该前驱节点ws已经被取消,循环则是继续往前找到未被取消的前驱节点重新连接队列
// 9.3 else 中则是将前驱节点的waitState改为Node.SIGNAL,待8中循环再次进入该方法时将符合9.1的条件
// 9.4 返回true:表示当前线程应该阻塞;返回false表示当前线程暂时不需要阻塞
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
// 10. AbstractQueuedSynchronizer.parkAndCheckInterrupt(),调用来源于8
// 10.1 LockSupport.park(),申请许可,无许可则阻塞当前线程,获得许可则继续执行10.2
// 10.2 返回当前线程的中断标志位,并清除当前线程的中断标志位,回到8
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
// 1. ReentrantLock.unlock()
public void unlock() {
sync.release(1);
}
// 2. AbstractQueuedSynchronizer.release()
// 2.1 h != null && h.waitStatus != 0,队列中头节点不为空,且waitStatus不为0才说明队列中有线程处于阻塞状态,故才需要尝试调用unpark方法
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
// 3. Sync.tryRelease()
// 3.1 因4.1加锁过程中5.2的重入锁情况,故state有可能不为1,故需要一个lock对应一个unlock才能将锁逐个解开,当c==0时,才会使得2流程中准备释放锁
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
// 4. AbstractQueuedSynchronizer.unparkSuccessor(),调用来源于2
// 4.1 ws < 0,表示后继节点已经准备好了,就等资源释放了
// 4.2 s == null || s.waitStatus > 0,表示当前节点已被删除或取消;将从队尾开始往前寻找第一个waitStatus<=0的节点
// 4.3 如果s不为空,则给s的线程发放许可证,使得s节点的线程可以继续调用加锁过程中10.2后的流程
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}