共计 15366 个字符,预计需要花费 39 分钟才能阅读完成。
AbstractQueuedSynchronizer,简称 AQS,是一个用于构建锁和同步器的框架。
JUC 包下常见的锁工具如 ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch 都是基于 AQS 实现的。
本文将介绍 AQS 的数据结构及独占模式的实现原理。
1. AQS 框架
AQS 所有操作都围绕着同步资源(synchronization state)来开展,解决了资源拜访的互斥和同步问题。
- 反对独占、共享形式来拜访资源。
- 对于无奈获取资源的线程,在同步队列中期待,直到获取资源胜利(或超时、中断)并出队。
- 线程胜利获取资源之后,若指定条件不成立,则开释资源并进入条件队列中期待,直到被唤醒,再转移到同步队列中期待再次获取资源。
AQS 框架将剩下的一个问题留给用户:获取、开释资源的具体形式和后果。
这其实是一种典型的模板办法设计模式:父类(AQS 框架)定义好骨架和外部操作细节,具体规定由子类去实现。
1.1 继承体系
AbstractQueuedSynchronizer 继承 AbstractOwnableSynchronizer,后者具备属性 exclusiveOwnerThread,用于记录独占模式下取得锁的线程。
public abstract class AbstractOwnableSynchronizer implements java.io.Serializable {
/**
* The current owner of exclusive mode synchronization.
*/
private transient Thread exclusiveOwnerThread;
}
AbstractQueuedSynchronizer 具备 ConditionObject 和 Node 两个外部类。
ConditionObject 是对 Condition 接口的实现,能够与 Lock 配合应用。
Node 是 AQS 中同步队列、条件队列的节点。
1.2 模板办法
AQS 定义了一系列模板办法如下:
// 独占获取(资源数)protected boolean tryAcquire(int arg) {throw new UnsupportedOperationException();
}
// 独占开释(资源数)protected boolean tryRelease(int arg) {throw new UnsupportedOperationException();
}
// 共享获取(资源数)protected int tryAcquireShared(int arg) {throw new UnsupportedOperationException();
}
// 共享获取(资源数)protected boolean tryReleaseShared(int arg) {throw new UnsupportedOperationException();
}
// 是否排它状态
protected boolean isHeldExclusively() {throw new UnsupportedOperationException();
}
Java 中罕用的锁工具都是基于 AQS 来实现的。
2. 数据结构
2.1 资源定义
锁和资源是同一个概念,是多个线程抢夺的对象。
AQS 应用 state 来示意资源 / 锁,通过内置的期待队列来实现获取资源 / 锁的排队工作。
期待队列(wait queue)是严格的 FIFO 队列,是 CLH 锁队列的变种。
因为 state 是共享的,应用 volatile 来保障其可见性,并提供了 getState/setState/compareAndSetState
三个办法来操作 state。
/**
* The synchronization state.
*/
private volatile int state;// 资源 / 锁
/**
* Returns the current value of synchronization state.
* This operation has memory semantics of a {@code volatile} read.
* @return current state value
*/
protected final int getState() {return state;}
/**
* Sets the value of synchronization state.
* This operation has memory semantics of a {@code volatile} write.
* @param newState the new state value
*/
protected final void setState(int newState) {state = newState;}
/**
* Atomically sets synchronization state to the given updated
* value if the current state value equals the expected value.
* This operation has memory semantics of a {@code volatile} read
* and write.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that the actual
* value was not equal to the expected value.
*/
protected final boolean compareAndSetState(int expect, int update) { // 原子操作
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
2.2 节点定义
AQS 的外部实现了两个队列:同步队列和条件队列。这两种队列都应用了 Node 作为节点。
节点的定义次要蕴含三局部内容:
- 节点的状态:SIGNAL、CANCELLED、CONDITION、PROPAGATE、0。
- 节点的模式:同步队列中的节点具备两种模式,独占(SHARED)、共享(EXCLUSIVE)。
- 节点的指向:同步队列是双向链表(prev/next),条件队列是单向链表(nextWaiter)。
节点的状态
- CANCELLED:值为 1,示意以后节点因为超时或中断被勾销。
- SIGNAL:值为 -1,唤醒信号,示意以后节点的后继节点正在期待获取锁。该状态下的节点在 release 或 cancel 时须要执行 unpark 来唤醒后继节点。
- CONDITION:值为 -2,示意以后节点为条件队列节点,同步队列的节点不会有这个状态。当节点从条件队列转移到同步队列时,状态会初始化为 0。
- PROPAGATE:值为 -3,只有共享模式下,同步队列的头节点才会设置为该状态(见 doReleaseShared),示意后继节点能够发动获取共享资源的操作。
- 0:初始状态,示意以后节点在同步队列中,期待获取锁。
java.util.concurrent.locks.AbstractQueuedSynchronizer.Node
static final class Node {
/** Marker to indicate a node is waiting in shared mode */
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
static final Node EXCLUSIVE = null;
/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/** waitStatus value to indicate the next acquireShared should unconditionally propagate */
static final int PROPAGATE = -3;
// 期待状态:SIGNAL、CANCELLED、CONDITION、PROPAGATE、0
volatile int waitStatus;
// 指向同步队列中的上一个节点
volatile Node prev;
// 指向同步队列中的下一个节点
volatile Node next;
volatile Thread thread;
// 在同步队列中,nextWaiter 用于标记节点的模式:独占、共享
// 在条件队列中,nextWaiter 指向条件队列中的下一个节点
Node nextWaiter;
/**
* Returns true if node is waiting in shared mode.
*/
// 节点模式是否为共享
final boolean isShared() {return nextWaiter == SHARED;}
/**
* Returns previous node, or throws NullPointerException if null.
* Use when predecessor cannot be null. The null check could
* be elided, but is present to help the VM.
*
* @return the predecessor of this node
*/
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
Node() { // Used to establish initial head or SHARED marker}
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
2.3 同步队列
同步队列是期待获取锁的队列,是一个双向链表(prev/next),应用 head/tail 执行队列的首尾节点。
java.util.concurrent.locks.AbstractQueuedSynchronizer
/**
* 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.
*/
// 期待队列的头节点,懒初始化。// 留神,如果头节点存在,那么它的 waitStatus 肯定不是 CANCELLED
private transient volatile Node head;
/**
* Tail of the wait queue, lazily initialized. Modified only via
* method enq to add new wait node.
*/
// 期待队列的尾节点,懒初始化。// 只能通过 enq 办法给期待队列增加新的节点。private transient volatile Node tail;
/**
* The synchronization state.
*/
private volatile int state;
在线程尝试获取资源失败后,会进入同步队列队尾,给前继节点设置一个唤醒信号后,通过 LockSupport.park(this)
让本身进入期待状态,直到被前继节点唤醒。
当线程在同步队列中期待,获取资源胜利后,通过执行 setHead(node)
将本身设为头节点。
同步队列的头节点是一个 dummy node,它的 thread 为空(某些状况下能够看做是代表了以后持有锁的线程)。
/**
* Sets head of queue to be node, thus dequeuing. Called only by
* acquire methods. Also nulls out unused fields for sake of GC
* and to suppress unnecessary signals and traversals.
*
* @param node the node
*/
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
AQS 不会在初始化队列的时候构建空的头节点(dummy node),而是在第一次产生争用时结构:
第一个线程获取锁,第二个线程获取锁失败入队,此时才会初始化队列,结构空节点并将 head/tail 指向该空节点。
具体见 AbstractQueuedSynchronizer#enq。
2.4 条件队列
条件队列是期待条件成立的队列,是一个单向链表(nextWaiter),应用 firstWaiter/lastWaiter 指向队列的首尾节点。
java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject
/** First node of condition queue. */
private transient Node firstWaiter; // 条件队列的头节点
/** Last node of condition queue. */
private transient Node lastWaiter; // 条件队列的尾节点
当线程获取锁胜利之后,执行 Conition.await(),开释锁并进入条件队列中期待,直到其余线程执行 Conition.signal 唤醒以后线程。
以后线程被唤醒后,从条件队列转移到同步队列,从新期待获取锁。
3. 独占模式
独占模式下,只有有一个线程占有锁,其余线程试图获取该锁将无奈取得成功。
3.1 获取锁 -acquire
独占模式下获取锁 / 资源,忽视中断,Lock#lock 的外部实现
java.util.concurrent.locks.AbstractQueuedSynchronizer#acquire
public final void acquire(int arg) {if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();}
- tryAcquire:尝试间接获取资源 / 锁,如果胜利则间接返回,失败进入下一步;
- addWaiter:获取资源 / 锁失败后,将以后线程退出同步队列的尾部,并标记为独占模式,返回新入队的节点;
- acquireQueued:使线程在同步队列期待获取资源,始终获取到后才返回,如果在期待过程中被中断过,则返回 true,否则返回 false。
- selfInterrupt:如果线程在期待过程中被中断过,在获取资源胜利之后,把中断状态补上。
3.1.1 tryAcquire
尝试获取资源,胜利返回 true。具体资源获取形式交由自定义同步器实现。
java.util.concurrent.locks.AbstractQueuedSynchronizer#tryAcquire
protected boolean tryAcquire(int arg) {throw new UnsupportedOperationException();
}
3.1.2 addWaiter
获取资源 / 锁失败后,将以后线程封装为新的节点,设置节点的模式(独占、共享),退出同步队列的尾部,返回该新节点。
java.util.concurrent.locks.AbstractQueuedSynchronizer#addWaiter
/**
* Creates and enqueues node for current thread and given mode.
*
* @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared // 独占模式、共享模式
* @return the new node
*/
// 从队列尾部入队
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); // tail 为空,入队
return node; // 返回以后的新节点
}
enq
从同步队列的尾部入队,如果队列不存在则进行初始化。
java.util.concurrent.locks.AbstractQueuedSynchronizer#enq
/**
* Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
*/
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)) { // 队列不为空,则以后节点作为新的 tail // CAS 失败,可能会呈现尾分叉的景象,由下一次循环打消分叉
t.next = node; // 因为不是原子操作,入队操作先设置 prev 指针,再设置 next 指针,会导致并发状况下无奈通过 next 遍历到尾节点
return t; // 返回以后节点的上一个节点(旧的尾节点)}
}
}
}
留神:
- 当第一次产生争用时,抢夺锁失败的线程入队,会先结构空节点(dummy node)作为 head/tail 节点进行初始化队列,再从队列尾部入队。
- 入队时,顺次设置 node.prev、tail、pred.next 指针,是非原子操作。
- 设置 prev 之后,若 CAS 设置 tail 失败,阐明其余线程先一步入队了,此时进入下一次循环会修改 prev 的指向。
- 因为入队是非原子操作,并发状况下可能无奈从 head 开始通过 next 遍历到尾节点 tail,然而从尾节点 tail 开始通过 prev 向前遍历能够拜访到残缺的队列。
3.1.3 acquireQueued
在同步队列自旋、期待获取资源直到胜利,返回期待期间的中断状态。
java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireQueued
/**
* Acquires in exclusive uninterruptible mode for thread already in
* queue. Used by condition wait methods as well as acquire.
*
* @param node the node
* @param arg the acquire argument
* @return {@code true} if interrupted while waiting
*/
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)) { // 上一个节点如果是头结点,阐明以后节点的线程能够尝试获取锁资源
// 获取锁胜利,以后节点作为新的头节点,并且清理掉以后节点中的线程信息(也就是说头节点是个 dummy node)// 这里不会产生争用,不须要 CAS
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 上一个节点不是头节点,或者以后节点的线程获取锁失败,须要判断是否进入阻塞:// 1. 不能进入阻塞,则重试获取锁。2. 进入阻塞
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt()) // 阻塞以后线程,当从阻塞中被唤醒时,检测以后线程是否已中断,并革除中断状态。接着持续重试获取锁。interrupted = true; // 标记以后线程已中断(如果线程在阻塞时被中断唤醒,会重试获取锁直到胜利之后,再响应中断)}
} finally {if (failed) // 自旋获取锁和阻塞过程中产生异样
cancelAcquire(node); // 勾销获取锁
}
}
在 acquireQueued 办法中,线程在自旋中次要进行两个判断:
- 是否获取锁
- 是否进入阻塞
具体代码流程:
- 在同步队列中自旋,若判断前继节点为头节点,则以后节点尝试获取锁。
- 若以后线程获取锁胜利,则将以后节点设为头节点,返回以后线程的中断状态。
- 若以后线程无奈获取锁、获取锁失败,则判断是否进入阻塞。
- 如果无奈进入阻塞,则持续自旋,否则进入阻塞。
- 线程从阻塞中被唤醒后,查看并标记线程的中断状态,从新进入自旋。
shouldParkAfterFailedAcquire
以后节点获取锁失败之后,通过校验上一个节点的期待状态,判断以后节点是否进入阻塞。
返回 true,可进入阻塞;返回 false,不可进入阻塞,需重试获取锁。
java.util.concurrent.locks.AbstractQueuedSynchronizer#shouldParkAfterFailedAcquire
/**
* Checks and updates status for a node that failed to acquire.
* Returns true if thread should block. This is the main signal
* control in all acquire loops. Requires that pred == node.prev.
*
* @param pred node's predecessor holding status
* @param node the node
* @return {@code true} if thread should block
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release // 以后节点曾经给它的上一个节点设置了唤醒信号
* to signal it, so it can safely park. // 以后节点能够进入阻塞
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and // 上一个节点状态大于 0,阐明是已勾销状态 CANCELLED,不会告诉以后节点
* indicate retry. // 则始终往前找到一个期待状态的节点,并排在它的后边
*/ // 以后节点不能进入阻塞,需重试获取锁
do {node.prev = pred = pred.prev; // pred = pred.prev; node.prev = pred; // 跳过上一个节点,直到找到 waitStatus > 0 的节点} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we // 上一个节点状态等于 0 或 PROPAGATE,阐明正在期待获取锁 / 资源
* need a signal, but don't park yet. Caller will need to // 此时须要给上一个节点设置唤醒信号 SIGNAL,但不间接阻塞
* retry to make sure it cannot acquire before parking. // 因为在阻塞前调用者须要重试来确认它的确不能获取资源
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL); // 通过 CAS 将上一个节点的状态改为 SIGNAL
}
return false;
}
以后节点可能进入阻塞的条件是:具备其余线程来唤醒它。
通过设置上一个节点状态为 SIGNAL,以确保上一个节点在开释锁之后,可能唤醒以后节点。
分为三种状况:
- 上一个节点状态为 Node.SIGNAL,阐明以后节点已具备被唤醒的条件,能够进入阻塞。
- 上一个节点状态为已勾销,则把以后节点排到未勾销的节点前面,持续自旋不进入阻塞。
- 上一个节点状态为 0 或 PROPAGATE,阐明正在期待获取锁,则以后节点将上一个节点设为 SIGNAL,持续自旋不进入阻塞。
parkAndCheckInterrupt
进入阻塞,阻塞完结后,查看中断状态。
java.util.concurrent.locks.AbstractQueuedSynchronizer#parkAndCheckInterrupt
/**
* Convenience method to park and then check if interrupted
*
* @return {@code true} if interrupted
*/
private final boolean parkAndCheckInterrupt() {LockSupport.park(this);
return Thread.interrupted();}
cancelAcquire
线程在 acquireQueued 中自旋尝试获取锁的过程中,如果产生异样,会在 finally 代码块中执行 cancelAcquire,终止获取锁。
/**
* Cancels an ongoing attempt to acquire.
*
* @param node the node
*/
private void cancelAcquire(Node node) { // 勾销获取锁
// Ignore if node doesn't exist
if (node == null)
return;
node.thread = null;
// Skip cancelled predecessors // 跳过已勾销的前继节点,为以后节点找出一个无效的前继节点
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
// predNext is the apparent node to unsplice. CASes below will
// fail if not, in which case, we lost race vs another cancel
// or signal, so no further action is necessary.
Node predNext = pred.next;
// Can use unconditional write instead of CAS here. // 写操作具备可见性(volatile),因而这里无需应用 CAS
// After this atomic step, other Nodes can skip past us. // 把以后节点设为已勾销之后,其余节点寻找无效前继节点时会跳过以后节点
// Before, we are free of interference from other threads.
node.waitStatus = Node.CANCELLED;
// If we are the tail, remove ourselves.
if (node == tail && compareAndSetTail(node, pred)) { // 如果是尾节点,则出队
compareAndSetNext(pred, predNext, null);
} else { // 进入这里,阐明不是尾节点,或者是尾节点但出队失败,须要解决后继节点
// If successor needs signal, try to set pred's next-link // 如果后继节点须要失去告诉,则尝试给它找一个新的前继节点
// so it will get one. Otherwise wake it up to propagate. // 否则把后继节点唤醒
int ws;
if (pred != head && // 前继节点不是头节点
((ws = pred.waitStatus) == Node.SIGNAL || // 前继节点的状态为 SIGNAL 或者 前继节点的状态为未勾销且尝试设置为 SIGNAL 胜利
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0) // 后继节点存在且未勾销
compareAndSetNext(pred, predNext, next); // 给后继节点设置一个新的前继节点(即后面找的无效节点),以后节点出队
} else {unparkSuccessor(node); // 如果存在后继节点,这里阐明无奈给后继节点找到新的前继节点(可能前继节点是 head,或者前继节点生效了),间接唤醒该后继节点
}
node.next = node; // help GC
}
}
节点 node 勾销获取锁,阐明以后节点 node 状态变为已勾销,成为一个有效节点。
须要思考如何解决节点 node 的后继节点:
- 无后继节点,则须要将最初一个无效节点(waitStatus <= 0)设为 tail。
- 存在后继节点,则须要将它挂在最初的无效节点之后,后续由该节点来唤醒后继节点。
- 存在后继节点,且找不到无效的前继节点,则间接把该后继节点唤醒。
unparkSuccessor
唤醒以后节点的后继节点。
java.util.concurrent.locks.AbstractQueuedSynchronizer#unparkSuccessor
/**
* Wakes up node's successor, if one exists.
*
* @param node the node
*/
// 唤醒以后节点的后继节点
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus; // 如果以后节点的状态为已勾销,则不变;如果小于 0(有可能后继节点须要以后节点来唤醒),则清零。if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); // CAS 失败也无所谓(阐明后继节点的线程先一步批改了以后节点的状态),因为接下来会手动唤醒后继节点
Node s = node.next;
if (s == null || s.waitStatus > 0) { // 后继节点为空,或已勾销,则从 tail 开始向前遍历无效节点
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t; // // 留神! 这里找到了之后并没有 return, 而是持续向前找
}
if (s != null)
LockSupport.unpark(s.thread); // 唤醒后继节点(或者是队列中距离 head 节点最近的无效节点)的线程
}
通常状况下, 要唤醒的节点就是本人的后继节点。如果后继节点存在且也在期待锁, 那就间接唤醒它。
然而有可能存在 后继节点勾销期待锁 的状况,此时从尾节点开始向前找起, 直到找到间隔 head 节点最近的未勾销的节点,对它进行唤醒。
为什么不从以后节点向后遍历无效节点呢?
- 以后节点可能是尾节点,不存在后继节点。
- 入队时先设置 prev 指针,再设置 next 指针(见 AbstractQueuedSynchronizer#enq),是非原子操作,依据 prev 指针往前遍历比拟精确。
3.2 获取锁 -acquireInterruptibly
比照 acquire,两者对获取锁过程中产生中断的解决不同。
- acquire 期待锁的过程产生中断,会等到获取锁胜利之后,再解决中断。
- acquireInterruptibly 期待锁的过程产生中断,会立刻抛出 InterruptedException,不再期待获取锁。
java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireInterruptibly
public final void acquireInterruptibly(int arg)
throws InterruptedException {if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
java.util.concurrent.locks.AbstractQueuedSynchronizer#doAcquireInterruptibly
/**
* Acquires in exclusive interruptible mode.
* @param arg the acquire argument
*/
private void doAcquireInterruptibly(int arg)
throws InterruptedException {final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {for (;;) {final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {setHead(node);
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException(); // 线程在阻塞期待锁的过程中,被中断唤醒,则放弃期待锁,间接抛出异样}
} finally {if (failed)
cancelAcquire(node);
}
}
3.3 开释锁 -release
独占模式下开释锁 / 资源,是 Lock#unlock 的外部实现。
java.util.concurrent.locks.AbstractQueuedSynchronizer#release
public final boolean release(int arg) {if (tryRelease(arg)) { // 开释锁资源
Node h = head;
if (h != null && h.waitStatus != 0) // head.waitStatus == 0,阐明 head 节点后没有须要唤醒的节点
unparkSuccessor(h); // 唤醒 head 的后继节点
return true;
}
return false;
}
- tryRelease:开释锁资源,开释胜利则进入下一步。
- unparkSuccessor:如果同步队列的头节点存在且满足 waitStatus != 0,则唤醒后继节点。
java.util.concurrent.locks.AbstractQueuedSynchronizer#tryRelease
protected boolean tryRelease(int arg) {throw new UnsupportedOperationException();
}
头节点 h 的状态:
- h.waitStatus == 0,同步队列中的节点初始状态为 0,阐明没有须要唤醒的后继节点。
- h.waitStatus < 0,独占模式下,阐明是 SIGNAL 状态,此时具备后继节点期待唤醒,见 AbstractQueuedSynchronizer#shouldParkAfterFailedAcquire。
- h.waitStatus > 0,阐明是 CANCELLED 状态,头节点是由勾销获取锁的节点而来的,保险起见,查看有没有须要唤醒的后继节点。
相干浏览:
浏览 JDK 8 源码:AQS 中的独占模式
浏览 JDK 8 源码:AQS 中的共享模式
浏览 JDK 8 源码:AQS 对 Condition 的实现
作者:Sumkor