Provides a framework for implementing blocking locks and related synchronizers (semaphores, events, etc) that rely on first-in-first-out (FIFO) wait queues. This class is designed to be a useful basis for most kinds of synchronizers that rely on a single atomic int value to represent state. Subclasses must define the protected methods that change this state, and which define what that state means in terms of this object being acquired or released. Given these, the other methods in this class carry out all queuing and blocking mechanics. Subclasses can maintain other state fields, but only the atomically updated int value manipulated using methods getState, setState and compareAndSetState is tracked with respect to synchronization.
提供了实现依赖于先进先出(FIFO)等待队列的阻塞锁和相关同步器(如信号量、事件等)的框架。此类旨在成为大多数依赖于单个原子 int 值表示状态的同步器的基础。子类必须定义受保护的方法,这些方法用于更改此状态,并定义该状态在获取或释放此对象时的含义。在定义了这些方法之后,此类中的其他方法将处理所有的排队和阻塞机制。子类可以维护其他状态字段,但只有通过 getState、setState 和 compareAndSetState 方法原子更新的 int 值才会在同步中被跟踪。
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
* 字段 waitStatus 的一个枚举值,表明当前节点已经被取消了
* This node is cancelled due to timeout or interrupt.
* Nodes never leave this state. In particular,
* a thread with cancelled node never again blocks.
* 这个节点由于超时或者中断被取消了,变为这个状态以后就不会再变了
* 尤其是被取消的节点永远不会再次被阻塞
*/
static final int CANCELLED = 1;
/**
* waitStatus value to indicate successor's thread needs unparking
* 字段 waitStatus 的一个枚举值,表明后继节点需要唤醒
* The successor of this node is (or will soon be)
* blocked (via park), so the current node must
* unpark its successor when it releases or
* cancels. To avoid races, acquire methods must
* first indicate they need a signal,
* then retry the atomic acquire, and then,on failure, block.
* 这个节点的后继节点已经被阻塞了,或者马上就要被阻塞了,(这里提到的阻塞
* 都是指通过LockSupport.part挂起了),那么当前节点在自己释放了同步,
* 或者自己被取消的时候,唤醒它的后继节点。为了避免竞争,acquire方法(获取
* 同步的方法)必须首先表明它们需要被唤醒(也就是将前驱节点标记为SIGNAL),
* 然后再去尝试原子获取同步,如果获取失败,阻塞
* 这里的需要被唤醒是指获取同步失败被阻塞后需要被前驱节点唤醒。
*
*/
static final int SIGNAL = -1;
/**
* waitStatus value to indicate thread is waiting on condition
* 字段 waitStatus 的一个枚举值,表明线程在等待一个
* This node is currently on a condition queue.
* It will not be used as a sync queue node
* until transferred, at which time the status
* will be set to 0. (Use of this value here has
* nothing to do with the other uses of the
* field, but simplifies mechanics.)
* 这个节点当前在一个 condition 队列中,直到被转移(转移到同步队列),
* 这个节点才会被当作一个同步队列的节点使用,这时waitStatus将会被
* 设置为0. 使用这个值与这个字段的其他使用无关,只是为了简化机制
*
*/
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*
* A releaseShared should be propagated to other
* nodes. This is set (for head node only) in
* doReleaseShared to ensure propagation
* continues, even if other operations have
* since intervened.
* 当一个线程释放共享后,应该被传播到其他节点。这是在 doReleaseShared
* 中设置的(只对头节点设置),确保传播的继续,即使自那以来有其他操作已经介入
* ”这部分指的是在 doReleaseShared 方法执行期间,可能存在其他线程的活动
* 或系统事件(如中断、上下文切换等),这些都可能影响同步器的状态或等待队
* 列的状态。然而,通过设置(在 doReleaseShared 中进行)来确保释放操作的
* 传播不受这些外部操作的影响,从而保持了同步器的稳定性和一致性。
*/
static final int PROPAGATE = -3;
/**
*
* The values are arranged numerically to simplify use.
* Non-negative values mean that a node doesn't need to
* signal. So, most code doesn't need to check for particular
* values, just for sign.
* 这些值是按照数值拍列的为了简化使用,非负数代表一个节点不需要唤醒
* 后继节点。因此在编码时大部分都不需要检查特殊的值,判断正负就可以了
* The field is initialized to 0 for normal sync nodes, and
* CONDITION for condition nodes. It is modified using CAS
* (or when possible, unconditional volatile writes).
* 对于常规的同步节点,这个字段会被初始化为0,条件节点初始化为
* CONDITION = -2 。改变是通过 cas 来改变的(或者可能的话,无条件volatile
* writes)
*/
volatile int waitStatus;
/**
* Link to predecessor node that current node/thread relies on
* for checking waitStatus. Assigned during enqueuing, and nulled
* out (for sake of GC) only upon dequeuing. Also, upon
* cancellation of a predecessor, we short-circuit while
* finding a non-cancelled one, which will always exist
* because the head node is never cancelled: A node becomes
* head only as a result of successful acquire. A
* cancelled thread never succeeds in acquiring, and a thread only
* cancels itself, not any other node.
* 这个字段指向前驱节点,当前线程(或者节点)依靠它来检查 waitStatus 的值,
* 在入等待队列的过程中赋值,一出队就置为null,为了方便垃圾回收。
* 同样当我们在寻找一个未取消的节点时,一旦一个前驱节点被取消了,我们就
* 短路(跳过被取消的节点),因为头节点永远不会被取消,所以总能找到一个未被
* 取消的节点(意思就是最差也能找到头节点这个未被取消的前驱节点)。
* 一个节点只有在 acquire 成功的情况下才会变成头节点,一个被取消的线程
* 永远不会 acquire 成功,一个线程只能取消它自己所属Node,不能取消任何其他
* Node
*/
volatile Node prev;
/**
* Link to the successor node that the current node/thread
* unparks upon release. Assigned during enqueuing, adjusted
* when bypassing cancelled predecessors, and nulled out (for
* sake of GC) when dequeued. The enq operation does not
* assign next field of a predecessor until after attachment,
* so seeing a null next field does not necessarily mean that
* node is at end of queue. However, if a next field appears
* to be null, we can scan prev's from the tail to
* double-check. The next field of cancelled nodes is set to
* point to the node itself instead of null, to make life
* easier for isOnSyncQueue.
* 这个字段指向后继节点,当前节点一旦 release ,就需要唤醒该后继节点。
* 该节点的后继节点入队过程中给其赋值,绕过取消的前驱节点时会对该字段
* 的指向进行调整,出队的时候将该字段职位null,为了方便垃圾回收。
* 入队(enq)操作在 attachment 之前是不会给next字段赋值的,这里的
* attachment 指的是将要入队Node的pre指向之前的尾节点,再通过cas将
* tail 指向要入队的节点,这两部合起来才算 attachment 成功。因此如果
* 看到一个Node的next==null,就没必要必须意味着这个节点是队列的尾节点,
* 换一个说法就是next==null 并不一定意味着是队列的尾节点,因为如果一
* 个节点刚完成 attachment 还未来得及将前驱节点next指向自己,那么它的
* 前驱节点的next还是null,但是完成了 attachment 之后就有可能有其他Node
* 追加到这个节点的后面。如果出现一个Node的next==null,我们可以从队列
* 尾部往前进行二次检测。被取消节点的next会指向节点自己而不是置为null,
* 使得 isOnSyncQueue 方法更简单高效
*/
volatile Node next;
/**
* The thread that enqueued this node. Initialized on
* construction and nulled out after use.
* 将当前节点入队的线程,创建节点对象的初始化,使用完了之后
* 置为null
*/
volatile Thread thread;
/**
* Link to next node waiting on condition, or the special
* value SHARED. Because condition queues are accessed only
* when holding in exclusive mode, we just need a simple
* linked queue to hold nodes while they are waiting on
* conditions. They are then transferred to the queue to
* re-acquire. And because conditions can only be exclusive,
* we save a field by using special value to indicate shared
* mode.
* 指向等待条件的下一个Node,或者指向特殊值SHARED。因为只有在持有
* 排他锁的情况下,才能够访问条件队列,我们只需要一个简单的链表队列
* 就可以持有正在等待某个条件的多个Node。这些等待条件的Node之后会被
* 转移到同步队列去重新获取锁。
* 因为条件只能是排他的,所以我们用一个字段保存一个特殊的值来表明共享
* 模式
*/
Node nextWaiter;
/**
* Returns true if node is waiting in shared mode.
* 如果节点正在以共享模式等待 返回 true
*/
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.
* 返回前驱节点,如果前驱节点为null,抛出空指针异常。当前驱节点不可能
* 为null的情况下,可以使用该方法。空检查可以省略,但存在是为了帮助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;
}
}
/**
* 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.
* 等待队列的头,延迟初始化。除了初始化,只可以通过setHead方法修改
* 注意:如果 head 存在,它的 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;
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
// 尾节点为null,说明队列还未初始化,这里进行初始化
// 创建一个空节点(节点的thread为null),waitStatus默认为0
// 将头和尾 都指向这个新创建的节点
// 这里使用 cas 的方式设置头节点,是为了保证只能有一个线程初始化
// 如果一个线程成功将 head 指向新创建的node,其他线程就会失败,只有
// 成功的线程才能进入 if 将tail指向这个节点。失败的线程进入下一次循环
// 执行 else 逻辑,成功的线程初始化完了之后,也进入下一次循环,执行else
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 这里先将要添加的Node的前驱节点指向尾节点 也就是前面提到的 attachment
// 操作的第一步
node.prev = t;
// 这里是 attachment 的第二部,通过 cas 将尾节点指向要添加的节点,这里使用
// cas 是因为可能同时有多个线程需要入队,多个线程可能都已经完成了 attachment
// 的第一步,都将要插入的Node的前驱节点指向同一个尾节点,所以这里必须保证只有
// 一个线程能够成功将 tail 指向自己要插入的节点,成功的节点会将前驱节点的next
// 指向自己,形成双向列表,其他线程都会失败,然后进入下次循环,拉去最新的尾节点
// ,重新执行上面的逻辑。
// 这里有个问题,就是通常我们判断一个列表的尾节点,是根据这个节点的next是否为null
// 如果为null,就认为是尾节点。但是这里会有一种特殊情况,就是一个节点的next为null
// 但是它却不是尾节点。具体的情况是,假如之前队列的尾节点是线程A添加的节点,这时B
// 线程要添加一个新节点,刚完成attachment,也就是B.pre=A,tail=B,但是还未完成
// A.next=B,这时A.next==null。这时有线程C也要添加新节点,发现tail==B,所以
// 直接就追加到B的后面。
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
java.util.concurrent.locks.AbstractQueuedSynchronizer#addWaiter
/**
* Creates and enqueues node for current thread and given mode.
* 为当前线程创建一个Node节点对象,并且入到等待队列,并且可以指定节点
* 是 独占模式 还是 共享模式
* @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
// 先尝试一次快速入队,如果快速入队失败了,就回到自旋入队
// 这里快速入队逻辑其实和自旋入队基本一样,只不过这里在判断尾节点不为null
// 也就是队列已经初始化情况下才会尝试快速入队,如果队列还未初始化,就要走
// 自旋入队方法 enq 的逻辑,enq 中会对队列进行初始化
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
//这里返回的就是新创建,并且完成入队的新节点
return node;
}
java.util.concurrent.locks.AbstractQueuedSynchronizer#setHead
/**
* 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.
* 将队列的head设置为传入的node节点,从而将原来的头节点出队。只会在
* aquire 方法中会被调用。同时也会将没用的字段置为null,为了垃圾
* 回收,并且可以抑制没必要的唤醒和遍历。
*
*
* @param node the node
*/
private void setHead(Node node) {
head = node;
// 因为node被设置为头节点,肯定是当前线程 acquire 成功了,有可能是成功
// 获取了独占锁,也有可能成功获取了共享锁,反正接下来就因该是获取锁的逻辑
// 返回,然后执行需要同步(需要上锁)的业务代码。所以这时候 node 节点就变成
// 了一个象征意义的头节点
// 执行完需要同步的代码,释放锁,这时候只需要通过头节点的next的找到下一个
// 等待获取锁的节点进行唤醒
// 介于上述,这里的 prev 置为null可以防止没必要的遍历
// thread 也不会再用到了 置为 null
node.thread = null;
node.prev = null;
}
java.util.concurrent.locks.AbstractQueuedSynchronizer#unparkSuccessor
/**
* Wakes up node's successor, if one exists.
* 这里是唤醒指定Node节点的后继节点所持有的线程,前提是这个节点有
* 后继节点,在释放锁之后进行,以便让其后继节点参与争取锁
* @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.
* 如果这个节点的 waitStatus 是一个负数(负数表示可能需要唤醒后继节点),
* 就尝试消除唤醒的期望,因为接下来的逻辑就是要完成唤醒后继节点,唤醒
* 后继节点之后,这个头节点就没用了,如果后继节点持有的线程成功获取到
* 锁,就会通过setHead()将头节点指向自己,这样node这个原来的head
* 就会被垃圾回收掉
* 这里就算修改失败或者 waitStatus 被等待线程修改了也没事
*
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
* 需要被唤醒的线程对象被后继节点所持有,正常情况下就是下一个,
* 但是如果遇到后继节点被取消,或者为null,这里的为null可能有
* 两种情况,一种是当前节点已经是尾节点,没有后继节点了,另一种
* 是后继节点还没来得及将其前驱节点的next指向自己。所以如果遇到
* 后继节点被取消或者后继节点为null的情况,就从尾部开始往回遍历
* 找一个确实没有被取消的后继节点
*/
// 这里申明的 s 就是要指向最终要唤醒的节点
Node s = node.next;
// s.waitStatus > 0 后继节点已经被取消
// s == null 没有后继节点或者后继节点还没有将前驱节点的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;
}
//s != null 说明找到了未取消的后继节点,对其所持有的线程进行唤醒
if (s != null)
LockSupport.unpark(s.thread);
}
java.util.concurrent.locks.AbstractQueuedSynchronizer#cancelAcquire
/**
* Cancels an ongoing attempt to acquire.
* 取消node节点所持有的线程正在进行的获取锁行为
* 该方法只在 acquire失败之后调用,
* 1.如果一个节点在等待锁时设置了超时时间,并且在超时时间内未能
* 获取到锁,该节点会被取消
* tryAcquireNanos 方法允许节点在尝试获取锁时设置超时时间。
* 如果在超时时间内未能获取到锁,节点会放弃等待,并返回 false
*
* 2.如果一个节点在等待锁时被中断,该节点会被取消
* acquireInterruptibly 方法允许节点在等待锁时被中断。
* 如果节点被中断,会抛出 InterruptedException,节点会放弃等待
*
* 3.如果一个节点在尝试获取锁时失败(例如,由于资源不足或其他原因),
* 该节点可能会被取消
* acquireQueued 方法种 tryAcquire 调用抛出异常
*
* @param node the node
*/
private void cancelAcquire(Node node) {
// Ignore if node doesn't exist
// 如果要取消的 node 为null 结束
// 这里不明白为啥要做这个校验,调用这个方法的地方都是通过new
// 创建的Node对象,不会是null
if (node == null)
return;
// 先把node节点所持有的线程置为null,因为节点被取消之后
// 会从队列中移除,node.thread = null 帮助垃圾回收
// 此时还未将节点的 waitStatus 修改为CANCELLED,还有可能
// 被其他线程进行没必要的唤醒,node.thread = null可以避免
// 这些没必要的唤醒
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.
// predNext 很明显是需要被移除的那个节点,这里有可能需要移除的节点
// 不止一个,从node到predNext都是需要被移除的节点,因为pred是从node
// 开始往前遍历遇到的第一个未取消的节点,所以他们之间的节点都是被取消的
// 如果不是,下面的cas将会失败,这种情况下说明我们在与其他 线程的取消
// 操作或者唤醒操作的竞争中输了,所以不需要再做其他操作了
// 换种说法就是,如果predNext不是需要被移除的节点,肯定是其他线程在
// 执行取消操作或者唤醒操作时已经将其移除了,这样下面的cas发现predNext
// 变了就会失败
Node predNext = pred.next;
// Can use unconditional write instead of CAS here.
// After this atomic step, other Nodes can skip past us.
// Before, we are free of interference from other threads.
// 在这里可以使用无条件写入而不是通过cas写入。因为这里的最终
// 目的就是要把node的状态修改尾CANCELLED,就算其他线程已经将其
// 状态修改,这里也可以将其覆盖。修改完了之后,其他线程在取消
// 或者唤醒操作时都会跳过我们,最终我们会被移除。
// 修改之前我们的状态还是默认值 0 ,所以不会受其他线程的干扰
// 也就是不会被其他线程移除。
node.waitStatus = Node.CANCELLED;
// If we are the tail, remove ourselves.
// 如果我们是尾节点,直接把我们移除就可以了,这里使用的删除方法
// 是,先通过cas将tail指向之前尾节点的前驱节点,如果成功,将其
// 前驱节点的next置为null,这样尾节点就没有被引用最后被垃圾回收
if (node == tail && compareAndSetTail(node, pred)) {
// 这里将前驱节点的next置为null,也是通过cas操作的,为了防止覆盖,
// 比如:我们第一步把tail指向前驱节点,还未完成将前驱节点的next置
// 为null的时候,正好有一个节点要入队,就把前驱节点当作tail追加在
// 其后面,并将前驱节点的next指向了新入队的节点,这时通过cas操作时,
// 所以就发现前驱节点的next已经改变了,所以就修改失败了,避免了将新
// 入队的节点丢失
compareAndSetNext(pred, predNext, null);
} else {
// 执行到这里有两种情况1.node本来就不是尾节点 2.node本来是尾节点,但是
// 在将尾节点指向其前驱节点之前,有其他线程的节点入队追加到到node后面
// 导致尾节点变成了新入队的节点,这样compareAndSetTail(node, pred)
// 就失败了。这两种情况下,node都会有后继节点
// If successor needs signal, try to set pred's next-link
// so it will get one. Otherwise wake it up to propagate.
// 接下来的逻辑就是判断如果node的后继节点需要被唤醒,就把前面找到那个
// 未取消前驱节点的next指向后继节点的,这样他将会收到一个前驱节点的唤醒
// ,这里的get one 就是获取到一个被唤醒的操作。
// 否则,唤醒后继节点以传播信号,这里的 it 指的是后继节点
int ws;
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
//执行到这里说明同时满足以下三个条件,那么前驱节点最后肯定能唤醒后继节点
//1.前驱节点不是头节点
//这里的原因是,如果前驱节点不是头节点,说明前驱节点有两种情况,
//第一种是正在阻塞等待它的前驱节点唤醒,他如果被唤醒抢到锁,执行完,释放
//锁的时候肯定也会唤醒它的后继节点
//第二种是,前驱节点正在获取锁或者刚获取到但是还没执行setHead方法
//,这种情况如果获取锁失败继续阻塞又和第一种一样了,如果获取锁成功了,
//说明被锁的代码块和释放锁的操作还没执行,所以它执行完了也会唤醒后继节点
//2.前驱节点的状态本来就是SIGNAL,或者被成功修改为SIGNAL
//3.pred.thread != null
//必须满足这个条件是因为,前驱节点如果是 阻塞被中断,或者阻塞获取锁的时间
//超时就有可能被取消,被取消的方法的首先会将被取消节点的thread置为null,
//所以这里通过判断pred.thread != null 来表示前驱节点未被取消
Node next = node.next;
// 这里判断后继节点存在且没有被取消,所以需要唤醒
// 这里也是通过cas修改,防止前驱节点的next已经被其他线程的 取消操作
// 或者 唤醒操作 指向了其他节点,已经不再是 predNext
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
//1.如果前驱节点为头节点
//2.如果pred.waitStatus != Node.SIGNAL
// 并且 compareAndSetWaitStatus(pred,ws,Node.SIGNAL)失败
//3.如果pred.thread == null
// 三个条件满足一个,唤醒被取消的节点 node 的后继节点
unparkSuccessor(node);
}
node.next = node; // help GC
}
}
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.
* 为获取锁失败的节点检查并更新状态
* 如果线程应该阻塞,返回true。 这是所有获取锁循环(自旋获取锁)中主要的
* 信号控制。
* 意思就是 如果返回 true,该线程接下来就会阻塞
* 如果返回 false,该线程本次循环不回阻塞,进入下次训话,再尝试获取一次锁
*
* @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.
* 这个节点已经设置为等待唤醒的状态,可以安心的挂起了
* 解释一下就是,该节点的前驱节点的状态为SIGNAL,前驱节点释放锁的时候
* 会唤醒它的后继节点(也就是当前节点)
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
* 执行到这里说明前驱节点已经被取消,跳过被取消的前驱节点(
* 如果有连续多个都是被取消的,都会被跳过),并指示重新获取锁,
* 这里的指示重新获取锁就是指返回false,下一次自旋循环。
* 进行下一次自旋循环的原因是,跳过取消的前驱节点之后,最终的前驱节点
* 是未被取消的,以下几种情况:
* 1.前驱节点正好是head,那么重新获取锁就有可能成功,未成功就再次执
* 行本方法,判断前驱节点状态为SIGNAL,返回true,被挂起。
* 2.前驱节点不是头节点,状态为SIGNAL,再次执行本方法,返回true,被挂起
* 3.前驱节点不是头节点,状态为 0 ,或者 PROPAGATE,再次执行本方法,进入
* else 逻辑,将前驱节点的状态改为 SIGNAL,返回false,进行下一次自旋循环
* ,如果前驱节点还不是头节点,再次执行本方法,判断前驱节点状态为SIGNAL,
* 返回true,被挂起
* 4.以上只说了被挂起的情况,如果下一次自旋循环时,正好前驱节点是头节点,并且
* 获取锁成功了,就不用执行本方法了,直接将本节点修改为head,然后执行本节点
* 所持线程的业务逻辑。
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
* 执行到这里前驱节点的的状态肯定是0 或者 PROPAGATE。表明当前节点
* 需要被唤醒,所以这里将前驱节点的状态修改为SIGNAL,确保前驱节点执行完
* 释放锁的时候能唤醒当前节点。但是当前节点还不会挂起(返回false),调用者
* (当前线程)再次自旋,确保在挂起之前没能获取到锁,意思就是再尝试一次获取锁
* ,如果获取失败,再挂起
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
/**
* Convenience method to interrupt current thread.
* 中断当前线程的一个简便方法
*/
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
/**
* Convenience method to park and then check if interrupted
* 挂起当前线程的简便方法,还会检查当前线程是否处于中断状态
* Thread.interrupted() 方法会清除掉线程的中断状态
* @return {@code true} if interrupted
*/
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireQueued
/*
* Various flavors of acquire, varying in exclusive/shared and
* control modes. Each is mostly the same, but annoyingly
* different. Only a little bit of factoring is possible due to
* interactions of exception mechanics (including ensuring that we
* cancel if tryAcquire throws exception) and other control, at
* least not without hurting performance too much.
* 这段代码注释说明了在并发编程中,获取锁(acquire)的多种方式,
* 这些方式在独占/共享和控制模式上有所不同。尽管每种方式在核心逻辑上大
* 致相同,但由于异常处理机制(包括确保在tryAcquire抛出异常时能够取消操作)
* 和其他控制逻辑的相互作用,使得它们之间存在一些令人烦恼的差异。
* 这些差异导致很难对这些获取锁的操作进行大量的代码复用,至少在
* 不影响性能太多的前提下是如此。
*
* acquire(int arg):
* 尝试获取独占锁,如果获取失败,将当前线程加入等待队列并等待。
* acquireInterruptibly(int arg):
* 尝试获取独占锁,并允许中断。如果线程被中断,抛出 InterruptedException。
* tryAcquireNanos(int arg, long nanosTimeout):
* 尝试获取独占锁,并设置超时时间。如果在超时时间内未能获取到锁,返回 false。
* acquireShared(int arg):
* 尝试获取共享锁,如果获取失败,将当前线程加入等待队列并等待。
* acquireSharedInterruptibly(int arg):
* 尝试获取共享锁,并允许中断。如果线程被中断,抛出 InterruptedException。
* tryAcquireSharedNanos(int arg, long nanosTimeout):
* 尝试获取共享锁,并设置超时时间。如果在超时时间内未能获取到锁,返回 false。
*
*/
/**
* Acquires in exclusive uninterruptible mode for thread already in
* queue. Used by condition wait methods as well as acquire.
* 以独占且不可中断模式为已经在队列中的线程获取锁。Condition.await()方法和
* 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) {
// 执行这个方法的时候其实node节点已经被加入到等待队列中了
// 这里主要处理的是这个节点的 尝试获取锁 挂起 和 被再次唤醒之后
// 的逻辑
// 默认获取锁会发生异常
boolean failed = true;
try {
boolean interrupted = false;
// 一个死循环,不断自旋尝试获取锁,如果失败检查是否需要挂起
// 如果被挂起,下次被唤醒后,重复同样逻辑
for (;;) {
final Node p = node.predecessor();
// 这里先判断前驱节点是head,才会尝试获取锁,
// 这个判断主要针对新入队的节点,或者跳过被取消的前驱节点
// 或者从tail往前找到第一个未被取消的节点的情况
// 正常情况下前驱节点不是head,当前节点是不会被唤醒的
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 如果前驱节点不是head,或者尝试获取锁失败了
// 检查是否需要挂起
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
// 如果该方法不是通过 return interrupted 的方式结束,
// 也就是获取锁失败了,比如,tryAcquire()中抛出异常
// 或者 获取锁超时 被中断
// 这里的failed == true,就会执行cancelAcquire()方法
if (failed)
cancelAcquire(node);
}
}
java.util.concurrent.locks.AbstractQueuedSynchronizer#tryAcquire
/**
* Attempts to acquire in exclusive mode. This method should query
* if the state of the object permits it to be acquired in the
* exclusive mode, and if so to acquire it.
* 尝试以排他模式获取锁,这个方法需要检查对象的状态是否容许以排他模式获取
* 锁,如果容许,就获取
*
* <p>This method is always invoked by the thread performing
* acquire. If this method reports failure, the acquire method
* may queue the thread, if it is not already queued, until it is
* signalled by a release from some other thread. This can be used
* to implement method {@link Lock#tryLock()}.
* 这个方法总是被执行acquire()方法的线程调用,如果这个方法返回失败false,
* 并且执行线程还未加入等待队列,acquire()方法可能会将执行线程放入等待队列,
* 直到被其他线程的release方法唤醒。
* 这个方法可以被用于实现Lock#tryLock()
*
*
* <p>The default
* implementation throws {@link UnsupportedOperationException}.
*
* @param arg the acquire argument. This value is always the one
* passed to an acquire method, or is the value saved on entry
* to a condition wait. The value is otherwise uninterpreted
* and can represent anything you like.
* @return {@code true} if successful. Upon success, this object has
* been acquired.
* @throws IllegalMonitorStateException if acquiring would place this
* synchronizer in an illegal state. This exception must be
* thrown in a consistent fashion for synchronization to work
* correctly.
* @throws UnsupportedOperationException if exclusive mode is not supported
*/
protected boolean tryAcquire(int arg) {
// 这里只是一个模板方法,需要子类自己根据需要去实现
throw new UnsupportedOperationException();
}
/**
* Attempts to set the state to reflect a release in exclusive
* mode.
* 尝试将状态设置为一个 独占模式下表示释放锁的值
*
*
* <p>This method is always invoked by the thread performing release.
* 这个方法总是被执行 release 的线程调用
*
* <p>The default implementation throws
* {@link UnsupportedOperationException}.
*
* @param arg the release argument. This value is always the one
* passed to a release method, or the current state value upon
* entry to a condition wait. The value is otherwise
* uninterpreted and can represent anything you like.
* @return {@code true} if this object is now in a fully released
* state, so that any waiting threads may attempt to acquire;
* and {@code false} otherwise.
* @throws IllegalMonitorStateException if releasing would place this
* synchronizer in an illegal state. This exception must be
* thrown in a consistent fashion for synchronization to work
* correctly.
* @throws UnsupportedOperationException if exclusive mode is not supported
*/
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
java.util.concurrent.locks.AbstractQueuedSynchronizer#acquire
/**
* Acquires in exclusive mode, ignoring interrupts. Implemented
* by invoking at least once {@link #tryAcquire},
* returning on success. Otherwise the thread is queued, possibly
* repeatedly blocking and unblocking, invoking {@link
* #tryAcquire} until success. This method can be used
* to implement method {@link Lock#lock}.
*
* 1.以独占模式获取锁:
* 独占模式意味着同一时间只有一个线程可以持有锁。
* 2.忽略中断:
* 在获取锁的过程中,线程不会响应中断请求。即使线程被中断,也不会抛出
* InterruptedException,而是继续尝试获取锁。
* 3.通过至少调用一次 tryAcquire 实现:
* 首先尝试调用 tryAcquire 方法来获取锁。如果成功,则立即返回。
* 4.如果成功则返回:
* 如果 tryAcquire 方法返回 true,表示成功获取锁,方法结束并返回,开始执行自己
* 的业务逻辑,不会再尝试加入等待队列
* 5.否则,线程将被加入队列(addWaiter()):
* 如果 tryAcquire 方法返回 false,表示未能获取锁,线程将被加入同步队列。
* 6.可能会反复阻塞和解除阻塞( acquireQueued() ):
* 线程在队列中等待时,可能会多次进入阻塞状态(通过 LockSupport.park 方法)
* 和解除阻塞状态(通过 LockSupport.unpark 方法)。
* 7.调用 tryAcquire 直到成功:
* 线程在每次解除阻塞后都会再次尝试调用 tryAcquire 方法,直到成功获取锁为止。
* 8.此方法可以用于实现 Lock#lock 方法:
* 这个方法的实现逻辑可以用于实现 Lock 接口中的 lock 方法,确保线程在获取
* 锁时的行为符合预期。
*
* @param arg the acquire argument. This value is conveyed to
* {@link #tryAcquire} but is otherwise uninterpreted and
* can represent anything you like.
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// 这里将当前线程修改为中断状态,原因可以看看acquireQueued()的返回值
// 能执行到这里说明 acquireQueued() 返回true,通过acquireQueued的逻辑
// 可以看出,parkAndCheckInterrupt()中在线程被唤醒后,检查当前线程的中断
// 状态,只有当前线程的中断状态是已中断(也就是在等待或者获取锁的过程中,
// 当前线程被中断),parkAndCheckInterrupt()才会返回true,interrupted
// 才会被修改为 true。这里 selfInterrupt() 相当于恢复当前线程的中断状态
selfInterrupt();
}
java.util.concurrent.locks.AbstractQueuedSynchronizer#release
/**
* Releases in exclusive mode. Implemented by unblocking one or
* more threads if {@link #tryRelease} returns true.
* This method can be used to implement method {@link Lock#unlock}.
* 独占模式下释放操作。释放操作的实现是 通过调用 {@link #tryRelease} 方法,
* 如果返回值为 true,则解除一个或多个线程的阻塞
*
*
* @param arg the release argument. This value is conveyed to
* {@link #tryRelease} but is otherwise uninterpreted and
* can represent anything you like.
* @return the value returned from {@link #tryRelease}
*/
public final boolean release(int arg) {
// 这里先判断 tryRelease 的结果,tryRelease返回true的情况下才会进行
// 唤醒操作
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
java.util.concurrent.locks.AbstractQueuedSynchronizer#doReleaseShared
/**
* Release action for shared mode -- signals successor and ensures
* propagation. (Note: For exclusive mode, release just amounts
* to calling unparkSuccessor of head if it needs signal.)
* 共享模式下的释放操作,唤醒后继节点并且保证唤醒操作传播(注意:对于独占
* 模式,只需要在头节点需要的情况下,对其执行unparkSuccessor )
*
*/
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
* 确保释放的操作能够传播,
*
* 1.确保释放操作能够传播:
* 释放操作不仅需要唤醒当前头节点的后继节点,还需要确保这种传播能够继续,
* 即使有其他线程正在进行获取或释放操作。
* 2.即使有其他正在进行的获取或释放操作:
* 在多线程环境中,可能会有多个线程同时尝试获取或释放锁。确保释放操
* 作能够正确传播是非常重要的。
* 3.这通常通过尝试唤醒头节点的后继节点来实现:
* 如果头节点状态为SIGNAL通过调用 unparkSuccessor 方法来唤醒头节点的后继节点,
* 使其有机会获取锁。
* 5.但如果头节点不需要信号,状态会被设置为 PROPAGATE:
* 如果头节点的后继节点不需要唤醒(例如,头节点状态为 0),则将头节点的状态设置为
* PROPAGATE,以确保在释放时传播继续。
* 6.此外,我们必须在一个循环中进行,以防在我们进行此操作时有新的节点被添加:
* 由于在多线程环境中,新的节点可能会在我们进行释放操作时被添加到队列中,
* 因此需要在一个循环中进行操作,以确保所有新添加的节点都能被正确处理。
* 7.另外,与 unparkSuccessor 的其他使用情况不同,我们需要知道 CAS
* 操作重置状态是否失败,如果失败则重新检查:
* 在 unparkSuccessor 方法中,通常不会检查 CAS 操作是否失败。但在这种情况下,
* 我们需要确保 CAS 操作成功,如果失败则重新检查状态,以确保释放操作能够正确传播。
*
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
// 这里将 h.waitStatus 修改为PROPAGATE有两个作用
// 1.如果有很多个线程同时执行释放操作,后来的线程就不用再尝试
// 这个修改操作,大大减少了修改的竞争,提高效率
// 2.如果本线程没有通过自旋,重新获取最新head的方式实现了释放的
// 传播,就需要被之前的线程唤醒的后继节点成功获取到锁后,通过
// setHeadAndPropagate 来实现释放的传播,setHeadAndPropagate
// 中会判断 setHead 之前之后 head.waitStatus < 0,才会调用传播
// 的相关操作,所以这里修改为PROPAGATE(-3),保证setHeadAndPropagate
// 中能够成功执行传播操作
// 根据相关资料,jdk1.7及之前的版本是没有 PROPAGATE 这个状态的,
// 是为了解决Semaphore中一个 在有可用锁资源的情况下不能及时传播释放
// 导致锁资源浪费,bug的id为 JDK-6801020
continue; // loop on failed CAS
}
// 执行到这里三种情况
// 1.h.waitStatus == Node.SIGNAL
// compareAndSetWaitStatus(h, Node.SIGNAL, 0) 成功将h的状态改为 0
// 执行了unparkSuccessor(h)
// 2.h.waitStatus == 0
// compareAndSetWaitStatus(h, 0, Node.PROPAGATE)成功将h的状态改为PROPAGATE,
// 这种情况的主要发生原因是,多个线程同时进行释放操作,只有一个线程可以
// h.waitStatus == Node.SIGNAL 并且compareAndSetWaitStatus(h, Node.SIGNAL,
// 0) 成功将h的状态改为 0,如果这个线程还未来得及调用unparkSuccessor()唤醒头
// 节点的后继节点,或者已经唤醒了头节点的后继节点,但是后继节点获取锁失败,或者
// 正在获取锁或者已经获取锁了,但是还未来得及执行setHeadAndPropagate 将head指向
// 头节点的后继节点。这时候其他线程也正好执行到这里,获取到的 h 还是之前的头节点,
// 所以这时 h.waitStatus==0,将h的状态改为PROPAGATE,这时如果再有其他线程也正好
// 执行到这里,因为h.waitStatus ==PROPAGATE,所以 if 和 else if 都不执行,直接
// 判断 头节点是否改变,如果没变循环结束该线程的释放操作就结束了,如果这时候之前
// 唤醒的线程刚好获取锁成功并将head指向了头节点的后继节点,那么这里发现头节点
// 改变了,就会重新拉取最新的头节点,进行同上的操作
// 3.h.waitStatus == PROPAGATE 或者 h.waitStatus == CONDITION 头节点不可能
// 为 CANCELLED
//
// loop if head changed 如果头节点改变了,继续循环,重新拉取最新的头节点
// ,进行同上的操作
// 正是通过这里,头节点变了,继续循环,对新的头节点操作的机制,实现了释放操作
// 的传播
if (h == head)
break;
}
}
java.util.concurrent.locks.AbstractQueuedSynchronizer#setHeadAndPropagate
/**
* Sets head of queue, and checks if successor may be waiting
* in shared mode, if so propagating if either propagate > 0 or
* PROPAGATE status was set.
* 设置等待队列的头节点,检查新的头节点的后继节点是否是共享模式,
* 如果是共享模式,那么无论 propagate > 0 (还有剩余的锁资源),或者
* 原来头节点被设置为PROPAGATE,h == null || h.waitStatus < 0 或者
* 新的头节点被设置为PROPAGATE (h = head) == null || h.waitStatus < 0
* 都要让释放操作进行传播
* @param node the node
* @param propagate the return value from a tryAcquireShared
*/
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);
/*
* Try to signal next queued node if:
* Propagation was indicated by caller,
* 释放操作的调用者指示需要传播。这意味着调用 releaseShared 方法时,
* 调用者希望释放操作能够继续传播,唤醒更多的等待线程。
* or was recorded (as h.waitStatus either before
* or after setHead) by a previous operation
* 或者在之前的某个操作中记录了传播标志。具体来说,头节点 h 的
* waitStatus 在 setHead 之前或之后被设置为 PROPAGATE
* 这里的 h 在setHead 之前是指向原来的head,setHead 之后是指向,被唤醒
* 后成功获取到锁,后面既需要校验原来的head,也需要校验改变后的head
* (note: this uses sign-check of waitStatus because
* PROPAGATE status may transition to SIGNAL.)
* 注意:这里使用 waitStatus 的符号检查,因为 PROPAGATE 状态可能
* 会转换为 SIGNAL 状态。这意味着 PROPAGATE 状态可能会被其他操作改查
* waitStatus 的符号来 变为 SIGNAL,因此需要检确定是否需要传播。
* 这里采用判断符号而不是判断具体的值,是因为 SIGNAL和 PROPAGATE都是
* 负数,这两种情况都需要继续传播。被唤醒的后继节点如果再次获取锁失败
* 会通过shouldParkAfterFailedAcquire 重新将头节点由0或者PROPAGATE
* 改为SIGNAL
*
* and
* The next node is waiting in shared mode,
* or we don't know, because it appears null
* 下一个节点正在以共享模式等待,或者我们不知道,因为它看起来是 null。
* 这意味着如果下一个 节点是共享模式的等待节点,或者下一个节点是 null,
* 则需要唤醒它。
* The conservatism in both of these checks may cause
* unnecessary wake-ups, but only when there are multiple
* racing acquires/releases, so most need signals now or soon
* anyway.
* 这两种检查的保守性可能会导致不必要的唤醒,但这只有在多个竞争的获取/释放
* 操作时才会发生,大多数情况下,这些线程很快就会需要被唤醒。
*/
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}
这里总结一下,根据上面的 releaseShared() doReleaseShared() doAcquireShared() setHeadAndPropagate() 的综合考虑,共享锁在释放时,主要在两个地方实现释放的传播
1.doReleaseShared() 如果发现head发生变化,说明被唤醒的后继节点成功获取到了共享锁,并且成功的将head指向自己,这种情况下,新head的后继节点也可能成功获取锁,所以继续循环,重新拉去最新的head,唤醒它的后继节点,实现释放的传播
2.setHeadAndPropagate 中被唤醒的后继节点如果发现,(1)还有剩余的锁资源 (2)head节点的 waitStatus < 0 ,就会尝试将释放操作传播的
这里顺便提一下,jdk1.6的有些版本没有 PROPAGATE 这个状态发生的一个bug JDK-6801020,https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6801020
bug的标题:Concurrent Semaphore release may cause some require thread not signaled
并发的Semaphore释放可能导致一些获取Semaphore的线程没有被唤醒
以下是跟这个bug相关的其他bug
Relates : | |
Relates : | |
Relates : | |
Relates : | JDK-7011859 - java/util/concurrent/Semaphore/RacingReleases.java failing |
Relates : | JDK-6956836 - java/util/concurrent/Semaphore/RacingReleases.java fails |
A DESCRIPTION OF THE PROBLEM : Semaphore initial state 0. 4 threads run 4 tasks. Two threads run acquire semaphore once, the other two threads run release semaphore once.One possible result is the semaphore state value is 1 and one thread still waiting. 创建Semaphore 并将state初始化为0,开启4个线程执行4个任务,两个线程各执行一次获取semaphore, 另外两个线程各执行一次释放semaphore,有一种可能的结果是:semaphore的state为1,并且有一个线程还在等待。 这里的 Semaphore 初始化state为0,state==0就表示没有任何的锁资源,这时候如果有两个线程进行acquire,都会获取失败,进入到等待队列。按理来说,等待队列中有两个等待节点,然后有两个线程执行释放操作,那么两个等待的节点都会被唤醒,如果还有一个线程一直等待说明出现问题了。
The possible reason: When AbstractQueuedSynchronizer#release are called, head.waitStatus may be 0 because the previous acquire thread may run at AbstractQueuedSynchronizer#doAcquireShared before setHeadAndPropagate is called. 这里直接翻译就是,如果前面的 acquire 线程被唤醒后重新开始执行doAcquireShared中的for循环刚成功获取到锁,还没有调用setHeadAndPropagate,这个时候 head.waitStatus 等于 0 第一个释放线程执行释放操作releaseShared时,会将head的waitStatus 改为0 ,并且唤醒head的后继节点,后继节点成功获取到锁,还未执行setHeadAndPropagate ,所以head还是指向之前的头节点,状态还是0。(调用 setHeadAndPropagate(node, r)的两个参数 r == 0, node为被唤醒的后继节点 , r就是setHeadAndPropagate中的propagate,propagate == 0,就不会唤醒node的后继节点) 这时候第二个释放线程执行释放操作releaseShared,由于h.waitStatus为0,所有没有执行unparkSuccessor(node)。 下面是bug版本的jdk 导致bug的主要方法
// 这是之前有bug的版本的代码
// jdk 6u11 版本中的实现
private void setHeadAndPropagate(Node node, int propagate) {
setHead(node);
// 这个 bug 就出现在这里,由于这里两个条件是 && 的关系
// 如果 propagate <= 0 ,就不会执行唤醒操作,这里的propagate
// 表示剩余的锁资源,这里propagate可能不是最新的剩余锁资源
if (propagate > 0 && node.waitStatus != 0) {
Node s = node.next;
if (s == null || s.isShared())
unparkSuccessor(node);
}
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
private void unparkSuccessor(Node node) {
compareAndSetWaitStatus(node , Node.SIGNAL , 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);
}
}
private void doAcquireShared(int arg) {
Node node= addWaiter(Node.SHARED);
try {
boolean interrupted= false;
while(true) {
Node p= node.predecessor();
if (p== head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null;
if (interrupted) {
selfInterrupt();
}
return;
}
}
if (shouldParkAfterFailedAcquire(p , node) &&
parkAndCheckInterrupt()) {
interrupted= true;
}
}
} catch (RuntimeException e) {
cancelAcquire(node);
throw e;
}
}
final class Node {
static final int CANCELLED = 1;
static final int SIGNAL = -1;
static final int CONDITION = -2;
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;
final boolean isShared() {
return this.nextWaiter == SHARED;
}
final Node predecessor() throws NullPointerException {
Node p= this.prev;
if (p== null) {
throw new NullPointerException();
} else {
return p;
}
}
Node() {
}
Node(Thread thread, Node mode) {
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread , int waitStatus) {
this.waitStatus = waitStatus;
this.thread = thread;
}
}
上面代码可以从这几个链接中下载
https://download.youkuaiyun.com/download/weixin_42286658/89893628
https://download.youkuaiyun.com/download/weixin_42286658/89893625
https://download.youkuaiyun.com/download/weixin_42286658/89893520
https://download.youkuaiyun.com/download/weixin_42286658/89893523
// REPRODUCIBILITY :This bug can be reproduced occasionally.
// 复现bug代码
import java.util.concurrent.Semaphore;
public class TestSemaphoreReproduced{
private static Semaphore sem = new Semaphore(0);
private static class Thread1 extends Thread {
@Override
public void run() {
sem.acquireUninterruptibly();
}
}
private static class Thread2 extends Thread {
@Override
public void run() {
sem.release();
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10000000; i++) {
Thread t1 = new Thread1();
Thread t2 = new Thread1();
Thread t3 = new Thread2();
Thread t4 = new Thread2();
t1.start();
t2.start();
t3.start();
t4.start();
t1.join();
t2.join();
t3.join();
t4.join();
System.out.println(i);
}
}
}
// I modified the test program a little to show the Semaphore
// state when it gets stuck.
// 这是bug评论区给出的一个优化代码,可以展示处程序被卡死时,state的值
import java.util.concurrent.Semaphore;
public class TestSemaphoreReproduced1{
private static Semaphore sem = new Semaphore(0);
private static class Blocker extends Thread {
@Override
public void run() {
sem.acquireUninterruptibly();
}
}
private static class Signaller extends Thread {
@Override
public void run() {
sem.release();
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10000000; i++) {
Thread b1 = new Blocker();
Thread b2 = new Blocker();
Thread t3 = new Signaller();
Thread t4 = new Signaller();
b1.start();
b2.start();
t3.start();
t4.start();
waitFor(b1);
waitFor(b2);
t3.join();
t4.join();
System.out.println(i);
}
}
static void waitFor(Thread t) throws InterruptedException {
t.join(30 * 1000);
if (t.isAlive()) {
System.out.printf("Semaphore stuck: permits %d, thread waiting %s%n",
sem.availablePermits(), sem.hasQueuedThreads() ? "true" : "false");
System.exit(-1);
}
}
}
本人没有在 jdk1.6 情况下,试过上面的复现bug的代码
如果有人想试 可以根据这篇 下载 jdk 历史版本的方法-优快云博客 下载 jdk 6u11 版本安装好进行测试
java.util.concurrent.locks.AbstractQueuedSynchronizer#doAcquireShared
/**
* Acquires in shared uninterruptible mode.
* @param arg the acquire argument
*/
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
// 往等待队列中追加一个共享节点
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 这个循环里的代码被执行到有两种情况
// 1.节点刚入队的时候
// 2.在等待队列中挂起后,又被唤醒
final Node p = node.predecessor();
if (p == head) {
// 如果当前线程发现前驱节点是头节点,说明自己有机会获取到
// 锁资源,尝试获取锁资源
int r = tryAcquireShared(arg);
if (r >= 0) {
// r >= 0 说明获取到了锁资源
// 将头节点指向当前节点,并且尝试将释放操作传播
setHeadAndPropagate(node, r);
p.next = null; // help GC
// interrupted == true 说明当前线程在被挂起等待的过程中
// 被中断,这里恢复它的中断状态
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
java.util.concurrent.locks.AbstractQueuedSynchronizer#tryAcquireShared
/**
* Attempts to acquire in shared mode. This method should query if
* the state of the object permits it to be acquired in the shared
* mode, and if so to acquire it.
* 尝试以共享模式获取许可资源。这个方法会先检查当前的state是否容许获取
* 共享锁,如果容许才会去获取
* 每一种共享锁获取到锁的场景和逻辑都不一样,所以具体的逻辑需要具体的共享锁
* 实现类去实现
* <p>This method is always invoked by the thread performing
* acquire. If this method reports failure, the acquire method
* may queue the thread, if it is not already queued, until it is
* signalled by a release from some other thread.
* 这个方法总是被执行 acquire 的线程调用,如果获取失败,并且acquire线程
* 还未加入到等待队列,它会被加入等待队列,一直等到有其他线程释放操作
* 将其唤醒
*
* <p>The default implementation throws {@link
* UnsupportedOperationException}.
*
* @param arg the acquire argument. This value is always the one
* passed to an acquire method, or is the value saved on entry
* to a condition wait. The value is otherwise uninterpreted
* and can represent anything you like.
* @return a negative value on failure; zero if acquisition in shared
* mode succeeded but no subsequent shared-mode acquire can
* succeed; and a positive value if acquisition in shared
* mode succeeded and subsequent shared-mode acquires might
* also succeed, in which case a subsequent waiting thread
* must check availability. (Support for three different
* return values enables this method to be used in contexts
* where acquires only sometimes act exclusively.) Upon
* success, this object has been acquired.
* @throws IllegalMonitorStateException if acquiring would place this
* synchronizer in an illegal state. This exception must be
* thrown in a consistent fashion for synchronization to work
* correctly.
* @throws UnsupportedOperationException if shared mode is not supported
*/
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
/**
* Attempts to set the state to reflect a release in shared mode.
* 尝试将state修改为 共享模式下 表示释放的值
* <p>This method is always invoked by the thread performing release.
*
* <p>The default implementation throws
* {@link UnsupportedOperationException}.
*
* @param arg the release argument. This value is always the one
* passed to a release method, or the current state value upon
* entry to a condition wait. The value is otherwise
* uninterpreted and can represent anything you like.
* @return {@code true} if this release of shared mode may permit a
* waiting acquire (shared or exclusive) to succeed; and
* {@code false} otherwise
* @throws IllegalMonitorStateException if releasing would place this
* synchronizer in an illegal state. This exception must be
* thrown in a consistent fashion for synchronization to work
* correctly.
* @throws UnsupportedOperationException if shared mode is not supported
*/
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireShared
/**
* Acquires in shared mode, ignoring interrupts. Implemented by
* first invoking at least once {@link #tryAcquireShared},
* returning on success. Otherwise the thread is queued, possibly
* repeatedly blocking and unblocking, invoking {@link
* #tryAcquireShared} until success.
* 1.以共享模式获取锁:
* 独占模式意味着同一时间只有多个线程可以持有锁。
* 2.忽略中断:
* 在获取锁的过程中,线程不会响应中断请求。即使线程被中断,也不会抛出
* InterruptedException,而是继续尝试获取锁。
* 3.通过至少调用一次 tryAcquireShared实现:
* 首先尝试调用 tryAcquireShared 方法来获取锁。如果成功,则立即返回。
* 4.如果成功则返回:
* 如果 tryAcquireShared() >= 0,表示成功获取锁,方法结束并返回,开始
* 执行自己的业务逻辑,不会再继续执行 doAcquireShared 尝试加入等待队列
* 5.否则,线程将被加入队列(addWaiter()):
* 如果 tryAcquireShared() < 0,表示未能获取锁,线程将被加入同步队列。
* 6.可能会反复阻塞和解除阻塞( doAcquireShared() ):
* 线程在队列中等待时,可能会多次进入阻塞状态(通过 LockSupport.park 方法)
* 和解除阻塞状态(通过 LockSupport.unpark 方法)。
* 7.调用 tryAcquireShared 直到成功:
* 线程在每次解除阻塞后都会再次尝试调用tryAcquireShared 方法,直到成功
* 获取锁为止。
* @param arg the acquire argument. This value is conveyed to
* {@link #tryAcquireShared} but is otherwise uninterpreted
* and can represent anything you like.
*/
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
java.util.concurrent.locks.AbstractQueuedSynchronizer#releaseShared
/**
* Releases in shared mode. Implemented by unblocking one or more
* threads if {@link #tryReleaseShared} returns true.
* 释放共享锁。通过调用 tryReleaseShared 返回true后唤醒一个或者多个
* 等待队列中的线程
*
*
* @param arg the release argument. This value is conveyed to
* {@link #tryReleaseShared} but is otherwise uninterpreted
* and can represent anything you like.
* @return the value returned from {@link #tryReleaseShared}
*/
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}