首先我们需要理解一些基本的概念,然后我们对AbstractQueuedSynchronizer 进行详尽的分析。
Cancellation即表示取消的机制,虽然AQS没有提供公开的cancel之类的方法,但是它却真实存在(可能有外包线程调用当前线程的中断方法), Cancellation 包括了中断引起的失败、其他失败 和超时引起的取消;正是Cancellation使得问题变得复杂, 因为它可能随时发生 ;
两个关键概念: 同步队列 和 条件队列。 这两者是绝对不同的东西,不能搞混!!引入这样的概念是很有必要的。
同步队列 命名来源于 sync queue; 为什么是sync,关键是对sync的理解,(这个词其实很难理解,英语意思是:同步;同步信号;同步域)表明同步代码相关的东西;
wait queue 表明是等待队列,它强调的是一种等待的状态,既可以是同步队列,也可以是条件队列; 依据当时的语境而定
一个锁对象 Lock(通常内部有一个AQS)中, 同步队列是 main queue,只会有一个; 同步队列位于Lock锁对象之上。
条件队列 位于锁对象创建的条件对象ConditionObject 之上。可以有很多个,因为lock对象可以随意的创建很多的条件!
特别注意: 条件队列 并不是直接位于Lock锁对象之上!!
spinForTimeoutThreshold 是一个非常短的时间, 如果超时时间小于这个时间,那么就不需要阻塞了,自旋一下即可。否则需要阻塞;———— 为什么呢,因为阻塞涉及到内核空间的调用,是个比较耗费性能的操作,应当尽量避免!
自旋: spin,看起来比较难理解; 其实就是说循环一下,就是说 让线程在循环中执行;执行什么?通常效果是无效的;而且需要做一些判断、检测,如果满足了条件,则停止循环; 这个循环,可以有各种条件,也可以是for(;;) 、while(true)或while(条件) 这种形式;———— 不管怎么说,循环肯定都是有判断或 中止条件的。
阻塞状态 其实又可以认为就是 等待状态;
Synchronizer 协调器;【电】同步器;整步器 、网络同步装置;同步器同步装置;同步机———— 最好还是理解为同步器
synchronization 同时;同时性;、同步化;同步控制;同步性 ———— 最好还是理解为同步机制
Lock ———— 最好还是理解为 同步锁
下面是 源码级别 逐字逐句 详尽分析!!!
/* * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.locks; import java.util.concurrent.TimeUnit; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import sun.misc.Unsafe; /** * 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 {@code 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 {@code int} * value manipulated using methods {@link #getState}, {@link * #setState} and {@link #compareAndSetState} is tracked with respect * to synchronization. * *共享模式类似,但可能涉及到级联信号)。 因为获取方法中的检查在入队之前,新获取的线程可以barge领先于 其他被阻止和排队的人。 不过,如果你愿意,可以。 定义{@code tryAcquire}和/或{@code tryAcquireShared}为 通过内部调用其中一个或多个检查功能来禁止驳运 方法,从而提供了一个公平的FIFO采集顺序。 特别是,大多数公平同步器可以定义{@code tryAcquire}。 返回{@code false},如果{@link #hasQueuedPredecessors}(一个方法) 专为公平同步器设计的)返回到 {@code true}。 其他的变化是可能的。Subclasses should be defined as non-public internal helper * classes that are used to implement the synchronization properties * of their enclosing class. Class * {@code AbstractQueuedSynchronizer} does not implement any * synchronization interface. Instead it defines methods such as * {@link #acquireInterruptibly} that can be invoked as * appropriate by concrete locks and related synchronizers to * implement their public methods. * *
This class supports either or both a default exclusive * mode and a shared mode. When acquired in exclusive mode, * attempted acquires by other threads cannot succeed. Shared mode * acquires by multiple threads may (but need not) succeed. This class * does not "understand" these differences except in the * mechanical sense that when a shared mode acquire succeeds, the next * waiting thread (if one exists) must also determine whether it can * acquire as well. Threads waiting in the different modes share the * same FIFO queue. Usually, implementation subclasses support only * one of these modes, but both can come into play for example in a * {@link ReadWriteLock}. Subclasses that support only exclusive or * only shared modes need not define the methods supporting the unused mode. * *
This class defines a nested {@link ConditionObject} class that * can be used as a {@link Condition} implementation by subclasses * supporting exclusive mode for which method {@link * #isHeldExclusively} reports whether synchronization is exclusively * held with respect to the current thread, method {@link #release} * invoked with the current {@link #getState} value fully releases * this object, and {@link #acquire}, given this saved state value, * eventually restores this object to its previous acquired state. No * {@code AbstractQueuedSynchronizer} method otherwise creates such a * condition, so if this constraint cannot be met, do not use it. The * behavior of {@link ConditionObject} depends of course on the * semantics of its synchronizer implementation. * *
This class provides inspection, instrumentation, and monitoring * methods for the internal queue, as well as similar methods for * condition objects. These can be exported as desired into classes * using an {@code AbstractQueuedSynchronizer} for their * synchronization mechanics. * *
Serialization of this class stores only the underlying atomic * integer maintaining state, so deserialized objects have empty * thread queues. Typical subclasses requiring serializability will * define a {@code readObject} method that restores this to a known * initial state upon deserialization. * *
Usage
* *To use this class as the basis of a synchronizer, redefine the * following methods, as applicable, by inspecting and/or modifying * the synchronization state using {@link #getState}, {@link * #setState} and/or {@link #compareAndSetState}: * *
*
* * Each of these methods by default throws {@link * UnsupportedOperationException}. Implementations of these methods * must be internally thread-safe, and should in general be short and * not block. Defining these methods is the only supported * means of using this class. All other methods are declared * {@code final} because they cannot be independently varied. * *- {@link #tryAcquire} *
- {@link #tryRelease} *
- {@link #tryAcquireShared} *
- {@link #tryReleaseShared} *
- {@link #isHeldExclusively} *
You may also find the inherited methods from {@link * AbstractOwnableSynchronizer} useful to keep track of the thread * owning an exclusive synchronizer. You are encouraged to use them * -- this enables monitoring and diagnostic tools to assist users in * determining which threads hold locks. * *
Even though this class is based on an internal FIFO queue, it * does not automatically enforce FIFO acquisition policies. The core * of exclusive synchronization takes the form: * *
* Acquire: * while (!tryAcquire(arg)) { * enqueue thread if it is not already queued; * possibly block current thread; * } * * Release: * if (tryRelease(arg)) * unblock the first queued thread; ** * (Shared mode is similar but may involve cascading signals.) * *Because checks in acquire are invoked before * enqueuing, a newly acquiring thread may barge ahead of * others that are blocked and queued. However, you can, if desired, * define {@code tryAcquire} and/or {@code tryAcquireShared} to * disable barging by internally invoking one or more of the inspection * methods, thereby providing a fair FIFO acquisition order. * In particular, most fair synchronizers can define {@code tryAcquire} * to return {@code false} if {@link #hasQueuedPredecessors} (a method * specifically designed to be used by fair synchronizers) returns * {@code true}. Other variations are possible. * *
Throughput and scalability are generally highest for the * default barging (also known as greedy, * renouncement, and convoy-avoidance) strategy. * While this is not guaranteed to be fair or starvation-free, earlier * queued threads are allowed to recontend before later queued * threads, and each recontention has an unbiased chance to succeed * against incoming threads. Also, while acquires do not * "spin" in the usual sense, they may perform multiple * invocations of {@code tryAcquire} interspersed with other * computations before blocking. This gives most of the benefits of * spins when exclusive synchronization is only briefly held, without * most of the liabilities when it isn't. If so desired, you can * augment this by preceding calls to acquire methods with * "fast-path" checks, possibly prechecking {@link #hasContended} * and/or {@link #hasQueuedThreads} to only do so if the synchronizer * is likely not to be contended. * *
This class provides an efficient and scalable basis for * synchronization in part by specializing its range of use to * synchronizers that can rely on {@code int} state, acquire, and * release parameters, and an internal FIFO wait queue. When this does * not suffice, you can build synchronizers from a lower level using * {@link java.util.concurrent.atomic atomic} classes, your own custom * {@link java.util.Queue} classes, and {@link LockSupport} blocking * support. /** 提供了一个实施封锁锁和相关的框架。 同步器(semaphores),依赖 先入先出(FIFO)等待队列。 这个类的设计是为了 是大多数依赖同步器的基础。 单个原子{@code int}值来表示状态。子类 必须定义改变这种状态的受保护方法,并且必须定义改变这种状态的 定义该状态对该对象的意义是什么? 或释放。 考虑到这些,这个类中的其他方法都携带了 出所有的排队和阻挡机制。子类可以保持 其他状态字段,但只有原子式更新的{@code int}。 使用方法{@link #getState}, {@link #setState}和{@link #compareAndSetState}被追踪到的是关于 到同步。
子类应定义为非公开的内部帮助程序。 类,用于实现同步属性。 其所附班级的。 类 {@codeAbstractQueuedSynchronizer}没有实现任何的 同步接口。 相反,它定义了一些方法,如 {@link #acquireInterruptibly},可以被调用为 具体的锁具和相关的同步器,以适当地 实现它们的公共方法。
这个类支持默认的排他性,也支持默认的排他性。 模式和共享模式。在独占模式下获得时。 其他线程试图的获取不能成功。共享模式 通过多线程获取可能(但不需要)成功。该类 不理解这些差异,但在下列情况下除外: 机械地认为,当共享模式获得成功后,下一个 等待的线程(如果有的话)也必须确定它是否能够 获得以及。在不同模式下等待的线程共享 同一FIFO队列。通常情况下,实现子类只支持 这些模式中的一种,但这两种模式都可以发挥作用,例如在 {@link ReadWriteLock}。只支持排他性的子类或 仅共享模式不需要定义支持未使用模式的方法。
这个类定义了一个嵌套的{@link条件对象}类,它是 子类可以作为{@link条件}的实现,通过子类 支持独占模式的方法{@link #isHeldExclusively}报告同步是否是专门的 对当前线程持有的方法{@link #release}。 调用当前{@link #getState}的值时完全释放了 这个对象,以及{@link #acquire},给定这个保存的状态值。 最终将此对象恢复到之前的获得状态。 没有 {@code AbstractQueuedSynchronizer}方法,否则就会创建这样一个 条件,所以,如果不能满足这个约束,就不要使用它。 的 {@link条件对象}的行为当然取决于 其同步器实现的语义。
该类提供了检测、仪表和监控功能。 内部队列的方法,以及类似的内部队列的方法。 条件对象。这些对象可以根据需要导出为类 使用{@code AbstractQueuedSynchronizer}为他们的 同步机理。
该类的序列化只存储底层原子的 整数维持状态,所以反序列化对象具有空的 线程队列。需要序列化的典型子类将 定义一个{@code readObject}方法,将其还原为已知的 在反序列化时的初始状态。
使用
。要使用该类作为同步器的基础,请重新定义 通过检查和/或修改,酌情采用以下方法: 1. 同步状态使用{@link #getState}, {@link #setState}和/或{@link #compareAndSetState}。
{@link #tryAcquire}{@link #tryAcquire}
这些方法中的每个方法默认都会抛出{@link UnsupportedOperationException}。 这些方法的实现 必须是内部线程安全的,一般来说应该是短的,而且 而不阻塞。定义这些方法是唯一支持的。 使用这个类的方法。所有其他的方法都声明 {@code final},因为它们不能独立变化。- {@link #tryRelease}{@link #tryRelease}
- {@link #tryAcquireShared}{@link #tryAcquireShared}
- {@link #tryReleaseShared}{@link #tryReleaseShared}
- {@link #isHeldExclusively}{@link #isHeldExclusively}
你也可以从{@link AbstractOwnableSynchronizer}有用的线程跟踪器。 拥有一个专属的同步器。 我们鼓励你使用它们 -- 这使得监测和诊断工具能够协助用户在以下方面进行监测和诊断: 确定哪些线程持有锁。
尽管这个类是基于内部FIFO队列,但它 并不自动执行FIFO获取策略。 核心的 独占方式同步如下:
Acquire。 while (!tryAcquire(arg)) { 如果线程还没有被排队,那么就取消对线程的请求。 可能阻止当前线程。 } 释放。 if (tryRelease(arg)) 解锁第一个排队的线程。
一般来说,吞吐量和可扩展性是最高的。 默认驳船(又称greedy。 放弃,和回避)策略。 虽然这不能保证公平或无饥饿感,但早期的 队列中的线程允许在后面的队列中的线程之前重新约定 每一次和解都有不偏不倚的成功机会 针对传入的线程。 此外,虽然获取方法不 "旋转"在通常的意义上,它们可以执行多个 调用{@code tryAcquire}的调用与其他 阻止之前的计算。 这就给了大部分的好处 独家同步时的旋转,只需短暂地举行一次,而不需 大部分的责任时,它不是。如果你愿意的话,可以 通过在前面调用获取方法时,用 "fast-path "检查,可能是预先检查{@link #hasContended}。 和/或{@link #hasQueuedThreads},只有在同步器的条件下,才会这样做。 很可能不会被争论。
这个类为我们提供了一个有效的、可扩展的基础。 同步化的部分原因是,将其使用范围专门用于 可以依赖{@code int}状态的同步器,获取、和 释放参数,以及内部FIFO等待队列。当这样做的时候 还不够,你可以从更低的层面上用 {@link java.utilite.concurrent.atomic atomic}类,你自己的自定义的 {@link java.utilite.Queueue}类,和{@link LockSupport}阻塞 支持。 * *
Usage Examples
* *Here is a non-reentrant mutual exclusion lock class that uses * the value zero to represent the unlocked state, and one to * represent the locked state. While a non-reentrant lock * does not strictly require recording of the current owner * thread, this class does so anyway to make usage easier to monitor. * It also supports conditions and exposes * one of the instrumentation methods: * *
{@code * class Mutex implements Lock, java.io.Serializable { * * // Our internal helper class * private static class Sync extends AbstractQueuedSynchronizer { * // Reports whether in locked state * protected boolean isHeldExclusively() { * return getState() == 1; * } * * // Acquires the lock if state is zero * public boolean tryAcquire(int acquires) { * assert acquires == 1; // Otherwise unused * if (compareAndSetState(0, 1)) { * setExclusiveOwnerThread(Thread.currentThread()); * return true; * } * return false; * } * * // Releases the lock by setting state to zero * protected boolean tryRelease(int releases) { * assert releases == 1; // Otherwise unused * if (getState() == 0) throw new IllegalMonitorStateException(); * setExclusiveOwnerThread(null); * setState(0); * return true; * } * * // Provides a Condition * Condition newCondition() { return new ConditionObject(); } * * // Deserializes properly * private void readObject(ObjectInputStream s) * throws IOException, ClassNotFoundException { * s.defaultReadObject(); * setState(0); // reset to unlocked state * } * } * * // The sync object does all the hard work. We just forward to it. * private final Sync sync = new Sync(); * * public void lock() { sync.acquire(1); } * public boolean tryLock() { return sync.tryAcquire(1); } * public void unlock() { sync.release(1); } * public Condition newCondition() { return sync.newCondition(); } * public boolean isLocked() { return sync.isHeldExclusively(); } * public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); } * public void lockInterruptibly() throws InterruptedException { * sync.acquireInterruptibly(1); * } * public boolean tryLock(long timeout, TimeUnit unit) * throws InterruptedException { * return sync.tryAcquireNanos(1, unit.toNanos(timeout)); * } * }}* *
Here is a latch class that is like a * {@link java.util.concurrent.CountDownLatch CountDownLatch} * except that it only requires a single {@code signal} to * fire. Because a latch is non-exclusive, it uses the {@code shared} * acquire and release methods. * *
{@code * class BooleanLatch { * * private static class Sync extends AbstractQueuedSynchronizer { * boolean isSignalled() { return getState() != 0; } * * protected int tryAcquireShared(int ignore) { * return isSignalled() ? 1 : -1; * } * * protected boolean tryReleaseShared(int ignore) { * setState(1); * return true; * } * } * * private final Sync sync = new Sync(); * public boolean isSignalled() { return sync.isSignalled(); } * public void signal() { sync.releaseShared(1); } * public void await() throws InterruptedException { * sync.acquireSharedInterruptibly(1); * } * }}* * @since 1.5 * @author Doug Lea */ AQS的关键属性: state。一般情况下state 是指资源,默认情况下当然state为0,普通竞争情况下可以表示等待锁的线程数量(假设一个线程最多只能获取一个资源) AQS的Node对象难以理解,尤其是其state的状态,哦不, 是waitStatus,是waitStatus默认是0,只允许存在5个值 ; 但是可以看到, 都是常量,除了CANCELLED,都是负数,分别什么意思呢? 为什么这样设计呢? public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable { private static final long serialVersionUID = 7373984972572414691L; /** * Creates a new {@code AbstractQueuedSynchronizer} instance * with initial synchronization state of zero. */ protected AbstractQueuedSynchronizer() { } /** * Wait queue node class. * *
The wait queue is a variant of a "CLH" (Craig, Landin, and * Hagersten) lock queue. CLH locks are normally used for * spinlocks. We instead use them for blocking synchronizers, but * use the same basic tactic of holding some of the control * information about a thread in the predecessor of its node. A * "status" field in each node keeps track of whether a thread * should block. A node is signalled when its predecessor * releases. Each node of the queue otherwise serves as a * specific-notification-style monitor holding a single waiting * thread. The status field does NOT control whether threads are * granted locks etc though. A thread may try to acquire if it is * first in the queue. But being first does not guarantee success; * it only gives the right to contend. So the currently released * contender thread may need to rewait. * *
To enqueue into a CLH lock, you atomically splice it in as new * tail. To dequeue, you just set the head field. *
* +------+ prev +-----+ +-----+ * head | | <---- | | <---- | | tail * +------+ +-----+ +-----+ 从上图来看,CLH锁,似乎只有一个prev,那么Node的next变量呢? ** *
Insertion into a CLH queue requires only a single atomic * operation on "tail", so there is a simple atomic point of * demarcation from unqueued to queued. Similarly, dequeuing * involves only updating the "head". However, it takes a bit * more work for nodes to determine who their successors are, * in part to deal with possible cancellation due to timeouts * and interrupts. * *
The "prev" links (not used in original CLH locks), are mainly * needed to handle cancellation. If a node is cancelled, its * successor is (normally) relinked to a non-cancelled * predecessor. For explanation of similar mechanics in the case * of spin locks, see the papers by Scott and Scherer at *
http://www.cs.rochester.edu/u/scott/synchronization/ * *We also use "next" links to implement blocking mechanics. * The thread id for each node is kept in its own node, so a * predecessor signals the next node to wake up by traversing * next link to determine which thread it is. Determination of * successor must avoid races with newly queued nodes to set * the "next" fields of their predecessors. This is solved * when necessary by checking backwards from the atomically * updated "tail" when a node's successor appears to be null. * (Or, said differently, the next-links are an optimization * so that we don't usually need a backward scan.) * *
Cancellation introduces some conservatism to the basic * algorithms. Since we must poll for cancellation of other * nodes, we can miss noticing whether a cancelled node is * ahead or behind us. This is dealt with by always unparking * successors upon cancellation, allowing them to stabilize on * a new predecessor, unless we can identify an uncancelled * predecessor who will carry this responsibility. * *
CLH queues need a dummy header node to get started. But * we don't create them on construction, because it would be wasted * effort if there is never contention. Instead, the node * is constructed and head and tail pointers are set upon first * contention. * *
Threads waiting on Conditions use the same nodes, but * use an additional link. Conditions only need to link nodes * in simple (non-concurrent) linked queues because they are * only accessed when exclusively held. Upon await, a node is * inserted into a condition queue. Upon signal, the node is * transferred to the main queue. A special value of status * field is used to mark which queue a node is on. * *
Thanks go to Dave Dice, Mark Moir, Victor Luchangco, Bill * Scherer and Michael Scott, along with members of JSR-166 * expert group, for helpful ideas, discussions, and critiques * on the design of this class. /** 等待队列节点类。
等待队列是 "CLH "的变体(Craig,Landin,和 Hagersten)锁队列。CLH锁通常用于 旋转锁。 我们反而用它们来阻断同步器,但 牵一发而动全身 节点的前辈中的线程信息。 每个节点中的 "状态 "字段记录了一个线程是否有一个 应该阻止。 当一个节点的前任 释放。 队列的每一个节点,否则就作为一个 专项通知式监控器,手持单机等待 线程。状态字段不控制线程是否为 授予锁等,但。 一个线程可以尝试获取,如果它是 拔得头筹 但第一并不能保证成功。 它只是赋予了竞争的权利。 所以,目前发布的 竞争者线程可能需要重新等待。
要入队到一个CLH锁,你可以将其原子化拼接为new 尾部。要取消队列,只需设置头部字段即可。
插入到CLH队列中只需要一个原子字段 尾巴 "上的操作,所以有一个简单的原子点的 从未排队到排队的分界。同样,取消排队 只涉及更新 "头"。然而,这需要一点 节点的工作更多的是确定谁是接班人。 部分原因是为了处理可能因超时而取消的问题。 和中断。
"prev "链接(原CLH锁中没有使用),主要是指的是 处理取消所需。如果一个节点被取消,其 继承者(通常)重新链接到一个未取消的 前任。对于类似的机理的解释 的自旋锁,见Scott和Schererer的论文,在
http://www.cs.rochester.edu/u/scott/synchronization/我们还使用 "下一个 "链接来实现阻塞机制。 每个节点的线程id都保存在自己的节点中,所以一个 前任的节点通过遍历 下一个链接来确定是哪条线程。 判断是哪个线程 继任者必须避开与新排队节点的比赛,以设置 的 "下一个 "领域的前辈们。 这个问题就解决了 必要时,从原子上倒查 当一个节点的继任者似乎是空的时候,更新 "尾巴"。 (或者换个说法,下一个链接是一种优化的方法。 这样我们通常就不需要逆向扫描了)。
取消引入了一些保守的基本的 算法。 由于我们必须对取消其他 节点,我们可能会错过注意到一个被取消的节点是否为 在我们的前面或后面。这个问题的处理方法是,始终不停车 取消后的继承人,使他们能够稳定在 新的前任,除非我们能确定一个未取消的 前辈将承担起这个责任。
CLH队列需要一个假头节点来启动。但是 我们不在施工中创造它们,因为这样做是浪费了 努力,如果从来没有争论的话。相反,节点 构建,并在第一时间设置头部和尾部指针。 争论。
等待条件的线程使用相同的节点,但 使用附加链接。条件只需要链接节点 在简单(非并发)链接队列中,因为它们是 只有在独占时才会被访问。 在等待时,一个节点被 插入到条件队列中。 一旦收到信号,该节点将 转移到主队列中。 状态的特殊值(即SHARED) 字段用于标记一个节点在哪个队列中。
感谢 Dave Dice、Mark Moir、Victor Luchangco、Bill Schererer和Michael Scott,以及JSR-166成员 专家组,以便提出有益的意见、讨论和批评。 关于本班的设计。 通过www.DeepL.com/Translator(免费版)翻译 首先每个Node表示一个线程,除了第一个线程,后面都是在等待资源的状态,对于它们而言,它们的state肯定都是SIGNAL ,除非被主动的cancel; 那么呢 SIGNAL 就是表示说, 我(某个node上的线程)正在等待之中,CLH队列前面的线程执行完毕之后,记得一定要通知我,不然我就会一直处于等待状态; ———— 这个过程一定是前面 CLH队列前面的线程 来进行通知,Doug Lea的设计就是这样的,当然我们可能也可以使用其它的机制,比如,每个线程自己主动的去轮训一个变量,但是这样显然 会有一个轮训时间的 间隔;必然不是大家能够接受的; 所以还是 被动的等待前个线程来唤醒来得自然,而且没有误差; ———— 又理解错了!! SIGNAL表示的是某个线程t的下一个线程需要t来唤醒! 跟上面的理解是相反的! 可以认为 SIGNAL 状态的node,它本身是什么状态呢,其实是不确定的,它自己可能也在等待状态,也可能正在运行之中,但肯定是没有被取消的! CONDITION 是表示某个线程t 处于 条件等待队列的状态; 这个比较特殊, 我一直都没有很好的理解; 非常坑爹! PROPAGATE 状态也是比较难理解; 它表示下一个acquireShared 方法应该无条件的传播(或者说扩散), 为什么没有说下一个线程,而是仅仅只方法,为什么无条件? 为什么需要传播? 什么是传播?
*/ static final class Node { 为什么 mode的类型仍然是 Node,而不是直接使用int数字呢?更新:因为nextWaiter字段需要在条件等待队列中试用,条件等待队列的元素仍然还是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; 为什么 EXCLUSIVE的值竟然是null? 太不可思议了! 不好理解啊!更新:: 其实这个时候 条件等待队列 还未开始建立起来,故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; /** * Status field, taking on only the values: * SIGNAL: 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. * CANCELLED: This node is cancelled due to timeout or interrupt. * Nodes never leave this state. In particular, * a thread with cancelled node never again blocks. * CONDITION: 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.) * 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. * 0: None of the above * * 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). */ waitStatus 需要仔细的解释, doc文档也进行了详细的解释:(可见它的重要和复杂程度 ) SIGNAL 当前node的继任者(也就是下一个node)应该是一件处于阻塞状态,或者很快将会被阻塞,为什么这里说“很快将”呢,有点难理解; 所以呢,它在它需要释放资源或取消的时候 唤醒它的继任者,不然就永远无法得以唤醒!!(因为也没有了其它的手段) 。 为了避免竞争,acquire 方法应该首先表明它们需要一个信号,然后重新执行原子性的acquire方法,然后如果acquire失败,那么阻塞;———— 这里又有些不好理解了,为什么需要事先表明一下呢? CANCELLED 表示当前node由于超时或者中断被取消了,node一旦处于这个状态,那么它永远也不会改变这个状态,而且会最终被删除,因为已经取消了,线程也无法再次得到执行。 CONDITION 表示node正处于一个条件队列,它将不会被用于同步队列,除非被转移到了同步队列;( 也就是说AQS 其实内部维护着两个队列,一个是同步队列sync queue ,另外一个是条件队列condition queue ),转移的时候,waitStatus 会被设置为0,为什么0,这是为了简化(具体如何简化,未知。。) PROPAGATE 表示 releaseShared 方法应该被传播扩散到其他的所有的node,这个状态由doReleaseShared 方法来进行设置,以确保传播扩散得以继续下去, (为什么需要传播扩散? ) 即使被其他操作干涉了。 有点难以理解 0 表示普通状态,也就是默认的状态吧。 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. 指向前任node,可以用来检查它的waitStatus。入队enq的时候进行赋值;仅在出队的时候进行GC nulled out;当前任node线程被取消时候,我们查找并重新设置一个未取消的node作为prev———— 这就是总是会成功的,因为head 也就是正在执行的线程 是永远不会被谁取消的,只有它去取消其他线程,而不是相反。只有成功获取资源之后一个node才会成为head,线程只会取消它自己, 不会被其他线程取消。———— 根本没有线程被其他线程取消的事情———— 好像跟自己之前的理解 有冲突。。 */ 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. 入队的时候赋值;绕过已取消节点或GC nulled out的时候进行调整, 入队方法enq并不会设置这个next字段(直到附件?),所以呢,如果看到某个node的next字段为null,也不一定就算说那个node处于CLH的尾部(??)然后,如果我们可以反向通过prev遍历.. 对于被取消的node, 它的next字段指向它自身而不是被设置为null, 这可以为isOnSyncQueue方法提供帮助。哦,原来如此! —— 这就是为什么unparkSuccessor 方法会反向通过prev指针遍历,因为next 靠不住啊... 更新: 这里的说法不准确,具体请查看unparkSuccessor 方法的更新 prev 是准确维护的,但是next 不是 */ volatile Node next; head tail 都是volatile 修饰的Node,但nextWaiter不是的,why? /** * The thread that enqueued this node. Initialized on * construction and nulled out after use. */ volatile Thread thread; 也是volatile,可能有多线程进行访问?也许吧 /** * 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. nextWaiter 也是Node类型变量,表明了下一个处于条件等待队列的node,或者特殊值 SHARED,(?更新: 这个英语的意思是: nextWaiter的值有两个可能: 1 执行下一个条件队列中等待的node, 2 特值SHARED) 因为条件等待队列只会存在于 独占模式下,我们只需一个 简单的 单链表 来保持那些真正条件等待的节点,它们会被转移到同步等待队列,然后重新获取资源,而且因为只能是独占模式,我们仅使用 特殊值 表明共享模式, 这个特殊值为什么是 SHARED 呢? 用来区分是否为共享模式,查看下面的isShared方法可知。 */ Node nextWaiter; AQS的条件等待队列,正是通过这个变量进行维护的!!!~~~。 /** * Returns true if node is waiting in shared mode. */ final boolean isShared() { return nextWaiter == SHARED; 通过nextWaiter是不是SHARED来判断是不是 共享模式~ 因为这里是直接比较地址, 所以 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检查本也可以被取消,但是它主要是为了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的三个构造方法,需要特别注意: Node() { // Used to establish initial head or SHARED marker } Node(Thread thread, Node mode) { // Used by addWaiter this.nextWaiter = mode; // Node表示了一个线程,nextWaiter也是Node类型,它指明了下一个等待的线程,为什么需要把下一个也设置出来呢? 为什么不是上一个? 一般线程不是被添加到CLH的末端吗? 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. */ 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; /** * 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); } // Queuing utilities /** * The number of nanoseconds for which it is faster to spin * rather than to use timed park. A rough estimate suffices * to improve responsiveness with very short timeouts. */ static final long spinForTimeoutThreshold = 1000L; /** * Inserts node into queue, initializing if necessary. See picture above. 插入node,如果有必须则先进行初始化 * @param node the node to insert * @return node's predecessor 返回入队成功后的 当前node的上一个节点 */ private Node enq(final Node node) { for (;;) { for循环保证了enq操作绝对会成功; Node t = tail; // 这里为什么需要引入局部变量t呢,而不直接使用tail 变量呢? 因为, if (t == null) { // Must initialize 第一次enq的时候,肯定t==null,那么需要设置tail = head if (compareAndSetHead(new Node())) // 这里采用了 cas 的方式设置head,也是保险起见,为什么呢,因为enq 方法是没有加锁的,不仅仅是enq方法,AQS整个类都没有加锁的地方,没有 synchronized 关键字,也没有其他锁机制,除了自身就剩下cas了,cas 是硬件实现的; 不能逻辑上保证只有一个一个线程执行到这里,所以这里采用了cas 需要特别注意的是, 这里的head 被设置为 new Node(), Node的这个构造器并没有指定一个线程,也是就没有线程,这么怎么理解呢? 可以认为 head 是当前获取锁成功,正在执行的那个线程,这是一个特例,故也不需要特别的设置这个node的对应的线程; 那么 当前执行enq 方法的线程,肯定是获取锁失败的,所以才会执行到这里的,所以呢, 这个仅仅是一个 初始化 CLH 的操作!! tail = head; 设置head成功之后,设置tail也指向head,因为此时head为null,当前CLH队列只有一个node,而既然CLH已经初始化了,那么tail head都是不能为空的,否则不好解释,说不过去。 } } else { node.prev = t; 把当前线程的prev指针执行tail, if (compareAndSetTail(t, node)) { 然后重新设置tail,也就是把tail指向当前线程, 为什么这里需要cas呢,因为执行到这里,仍然可能是多个线程的,因为确实存在竞争,保险起见,需要通过cas来设置; t.next = node;把tail 的 next设置为当前线程对应的node,即相当于把当前线程添加到了CLH的末尾; return t; 返回tail, } } } } /** * Creates and enqueues node for current thread and given mode. 创建node并且把它入队: 通过当前线程和制定的mode 来进行创建 * * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared mode 仅存在两个值,独占或者共享; 为什么 mode的类型仍然是 Node,而不是直接使用int数字呢? * @return the new node */ addWaiter 方法: 刚开始的时候 head tail 肯定是null,所以直接enq, private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); 通过当前线程和制定的mode 来进行创建 // Try the fast path of enq; backup to full enq on failure 对这个效果存疑,感觉没什么必要 Node pred = tail; if (pred != null) { 如果已经初始化过一次,那么就使用fast path方式,其实下面的4行代码和enq方法中的最后4行的效果是完全一模一样的!~ 这么做呢,仅仅是为了fast,其实我感觉也没有多少fast效果,直接调用enq方法不过是多了for包围.. node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } enq(node); return node; } /** * 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. 设置头结点,然后出队, 仅在acquire方法中被取消, 同时null out无用的字段,为了GC。以及抑制无必要的唤醒信号和遍历。 * * @param node the node */ private void setHead(Node node) { head = node; node.thread = null; 头结点的线程变量 无须设置,故干脆设置为null node.prev = null; 头结点的prev指针 必然是null } /** * 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; if (ws < 0) 如果是SIGNAL或者PROPAGATE,那么设置当前节点的waitStatus为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. 需要被unpark的线程位于后继node,填充就下一个,但是不排除其他的方法取消了它,那么需要反向遍历一下,找到还没有被取消的后继node。为什么需要反向呢?为什么不是直接通过next指针顺序遍历? 因为 prev指针比next可靠? 更新: 其实不是这样的.. 见下面if里面的分析 the next node 可能已经被取消,或者就是null(什么情况下为null?xxx 好像只有获取到执行完毕出队之后) */ Node s = node.next; if (s == null || s.waitStatus > 0) { s = null; 头结点的next变量即后继节点s 为null,或者其状态已经表明了它取消,故把它 设置为null, for (Node t = tail; t != null && t != node; t = t.prev) 为什么需要反向呢? 因为s都可能为null啊, 自然没法获取s的next, 那么保守起见,反向获取比较安全 if (t.waitStatus <= 0) s = t; } if (s != null) 找到了,那么就唤醒它吧 LockSupport.unpark(s.thread); node 的后继已经被唤醒,一般来说就表示了node对应的线程已经执行完毕,那么它应该立即出队,但是AQS不是这样做的, 而是呢,而是在后继node 醒来之后重新去获取资源,获取成功之后 才重新设置head,然后将当前node 出队。 具体参见 比如 acquireQueued } /** * 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.) 共享模式下释放操作,通知后继node,确保广播开来,(如果是独占模式,释放不多不少的资源量,然后如果有必要则执行head的解锁方法)? */ 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. 确保release操作也能够广播开来,即使其他的acquires/releases操作正在同步进行之中,如果有必要则对head的后继执行解锁方法,否则就设置head的状态为PROPAGATE 以确保在它释放资源的时候,广播操作仍会继续;另外,我们必须通过循环来进行这些操作以防执行当前方法的时候新的线程进入CLH尾部,同时,不同于其他情况的unparkSuccessor操作,我们需要在cas操作失败的时候进行重新检查 */ for (;;) { 自旋, cas 失败则需要进行自旋!! cas 失败说明其他线程也在修改同一个变量,但是它抢先修改成功了~~ Node h = head; if (h != null && h != tail) { 不为null,不是尾节点 int ws = h.waitStatus; if (ws == Node.SIGNAL) { 如果状态为SIGNAL,如果有必要则对head的后继执行解锁方法 if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) 把状态设置为0, 为什么要是0? 用以表明已经唤醒过一次后继了, 所以不再需要状态为SIGNAL了,否则还会进行下一次的unparkSuccessor continue; // loop to recheck cases 如果设置失败,跳出本次循环 unparkSuccessor(h); 设置成功,唤醒后继; 只要唤醒了一个后继,那么至少保证了不会死锁.. unparkSuccessor 方法其实也有一个cas设置waitStatus为0 的操作,所以,上面的if感觉不是很有必要?xxx 也不完全是,unparkSuccessor的cas 并没有强制的保证 } 否则就设置head的状态为PROPAGATE 以确保在它释放资源的时候,广播操作仍会继续 假设第一次自旋执行了上面的if,而且head没有发生变化,那么,第二次的自旋就肯定会执行到这里的else,那么肯定会把状态再次修改为PROPAGATE 更正: 如果head没有发生变化,这里的if else 之后执行一个,只会执行一次~! else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) 如果状态已经是0,那么把状态设置为PROPAGATE 什么情况下head的状态是0呢?队列中只有一个node,而且clh 可能还没初始化呢。为什么把head的状态设置为PROPAGATE就完事了呢? 其实setHeadAndPropagate 方法判断依据是<0,自然包括了 PROPAGATE,然后再执行 doReleaseShared,doReleaseShared会进行对后继的唤醒操作 continue; // loop on failed CAS 如果设置失败,跳出本次循环 } if (h == head) // loop if head changed 这个地方 也是难以理解 break; head changed 不会跳出循环,但是如果head没有变,那么不在进行自旋,因为该完成的操作绝壁都已经成功执行了。 但是如果head changed,那说明 当前方法执行期间,其他方法对head进行了 干扰, 那么自旋,确保无论如何都要进行广播,因为head已经在本轮循环很短的时间内就发生变化,那么重来 unparkSuccessor 不会改变head,改变head可能是另外的出队操作。比如,获取资源成功之后重新设置head.. 那么此时,需要重新的自旋,执行比如唤醒、设置状态等操作,以确保当前方法的逻辑正确,主要保证广播得以继续,其他的node 有机会共享的方式 同步执行.. 其实如果上面的unparkSuccessor之后,后继线程被唤醒得以执行,然后head 会很快发生变化,但是这个时间是不确定的。 } } /** * 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. 设置head为当前线程node,并且如果有剩余(资源剩余量大于0)或者waitStatus已经是PROPAGATE ,而且后继node处于共享模式等待状态,那么尝试传播扩散 * * @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);// 直接设置,无须通过cas,why?因为。。 /* * Try to signal next queued node if: * Propagation was indicated by caller, * or was recorded (as h.waitStatus either before * or after setHead) by a previous operation * (note: this uses sign-check of waitStatus because * PROPAGATE status may transition to SIGNAL.) 尝试发送信号给(就是唤醒的意思)下一个已经入队的node,如果它已经被或者将要被设置为PROPAGATE, 注意,这里对 waitStatus采用了sign-check,因为PROPAGATE状态可能转换为SIGNAL * and * The next node is waiting in shared mode, * or we don't know, because it appears null 和,下一个node处于共享模式等待状态, 或者我们不知道,因为它为null(??大师都竟然不知道?)xxx * * 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. 保守性.稳健主义. 这些保守的检测可能会导致没有必要的唤醒,但是仅当多线程竞争acquires/releases时才会,所以大部分线程 now or soon确实需要信号 xxx */ h是旧head,setHead(node);方法之后,head已经发生变化,变成了当前node,那么 if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0) { 这里的判断有点复杂, 如果资源有剩余 或者头结点head为空,———— 可能被取消了吧? 或者head的waitStatus<0, ———— 表明需要一个信号来唤醒, 或者 新头结点head为空 或者 新头结点head为waitStatus<0 ———— 这样的写法有点不好理解。感觉太保守了。xxx 可是, Node s = node.next; 那么获取当前节点的下一个, if (s == null || s.isShared()) 如果 为空,或者处于共享状态, doReleaseShared(); 那么 再次执行共享模式释放资源 } } // Utilities for various versions of acquire /** * Cancels an ongoing attempt to acquire. 取消仍在进行的获取的尝试动作 观察发现,这个方法无一例外都是 在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. predNext变量是显而易见需要被取消胶接的,否则下面的CAS方法就会失败,如果失败,我们就会失去同其他取消、信号操作的竞争(?) 所以不需要更多的其他操作..(?) 并发情况下 cas 是可能失败的,但是呢, 这里我们并没有使用 无限for循环+CAS,为什么呢?因为,如果cas失败,那么表明其他线程进入了这个cancelAcquire方法,那么它应该是成功了,只要有一个线程成功了,那么当前node就会成功被取消,那么自然也无须再继续的 for循环方式自旋.. 既然如此,那么就不使用cas方法了,可以吗? 感觉怪怪的。 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,只使用无条件的write写操作即可,因为它是原子性的。这行代码(write写操作)执行完之后,其他的node就可以跳过当前node了。之前呢,我们也避免了其他线程的干扰。(?) node.waitStatus = Node.CANCELLED; // If we are the tail, remove ourselves. 如果我们是尾巴,从队列中移除自己 if (node == tail && compareAndSetTail(node, pred)) { compareAndSetNext(pred, predNext, null);// 这里为什么有cas?这里的CAS为什么没有自旋? } else { // If successor needs signal, try to set pred's next-link // so it will get one. Otherwise wake it up to propagate. 如果后继node需要一个唤醒信号,那么把它设置为pred's next-link。否则直接传播扩散 唤醒它 int ws; if (pred != head && ((ws = pred.waitStatus) == Node.SIGNAL || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && pred.thread != null) { 前任状态为需要唤醒后继 或者 状态<0但设置前任的状态成功 并且 前任的thread 不为null (head的thread为null,) —— 难理解 其实可以这样理解,如果前任的状态为SIGNAL(或者它不为SIGNAL也没有取消,但是我设置它成功了) 并且前任的线程不为空,那么前任执行完肯定会唤醒当前node的后继,那么我就不要执行唤醒操作了。 Node next = node.next; if (next != null && next.waitStatus <= 0) compareAndSetNext(pred, predNext, next); 设置前任的next字段为当前的后继,相当于bypass了当前node! 这个操作失败了也不要紧,因为.. } else { 如果不满足条件,那么保守起见,我直接唤醒当前正在取消的线程的后继,免得它永远得不得唤醒 unparkSuccessor(node); } node.next = node; // help GC 这里有个奇怪的做法,仅仅是为了GC? 其实也可以理解,否则呢GC 可能一直都不会回收掉这个已经被取消的node 但为什么这里不设置 node的prev 呢? } } /** * 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. 检查并且更新获取资源失败的node的状态,如果应该被阻塞,那么返回true,这个方法是所有获取的循环中,主要的信号控制方式; 需要确保 pred == 当前node的上一个节点(这个要求应该是保险起见,为了避免执行过程中,pred突然被改变或取消了,那么就导致不满足 pred == node.prev,因为 node.prev总是最新的,但是pred 是旧的引用,可能已经过时.. ); 否则,当然是返回false * * @param pred node's predecessor holding status 当前node的前任,肯定是不为空的; holding status不好翻译 * @param node the node 当前node,那个线程执行这个方法,就是那个线程对应的node! * @return {@code true} if thread should block */ private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { 这个方法也是巨难理解 int ws = pred.waitStatus; 获取pred的等待状态变量,看它是具体处于一个什么状态; if (ws == Node.SIGNAL) 如果是SIGNAL,那么就是说前任节点的线程结束运行之后,肯定会主动来通知当前线程,那么我就可以安心的去进行阻塞了,那么直接返回true 为什么这么搞啊!! 第一次,pred 的waitStatus肯定不是 SIGNAL,所以不会走到这个if里面来,但是呢; 而且下面的if else 都是返回false,但是没关系啊,acquireQueued的无限for循环保证线程仍然会执行到这里^_^ /* * This node has already set status asking a release * to signal it, so it can safely park. 安心的park */ return true; if (ws > 0) { 第一次,pred 的waitStatus默认不是 > 0,但是不排除其他的方法取消了它。 注意,仅仅在CANCELLED状态下,即waitStatus = 1的时候,才会>0; 也就是说, 这里的 >0,可以改为 == CANCELLED; 是吧 /* * Predecessor was cancelled. Skip over predecessors and * indicate retry. 前任已经被取消,跳过所有被取消了的node,然后重新设置前任,直到它还没有被取消 */ do { node.prev = pred = pred.prev;// 同时设置一下当前node的prev指针。 } while (pred.waitStatus > 0); 用到了while循环。 pred.next = node; // 此时会返回一个false,但是没关系啊,acquireQueued 有个无限for循环,会再次进入此方法。 } else { 一般情况下,只要不是所有线程都被取消了,那么肯定会进这个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. // 这个时候, waitStatus 必然是0 或者PROPAGATE,那么就设置一下 pred 的waitStatus字段; 但是暂不阻塞park,为什么不阻塞呢? 需要再次重试, 也确保它阻塞之前无法获取资源。 所以呢,执行到这里,仍然是返回false, 那么没关系,acquireQueued方法会再次的执行判断pred 是不是head,如果是head,还会进行一次 tryAcquire 。 所以说,这里的retry并不是针对所有node,而是仅当pred为head才会,否则执行tryAcquire也是没有意义; 为什么需要考虑 PROPAGATE ,这个是共享锁的时候需要考虑的; 为什么不考虑 waitStatus为 CONDITION 状态? 因为 CONDITION 状态的node 是另外一个队列,不相关的机制 暂时无须考虑它,后面细说。 */ compareAndSetWaitStatus(pred, ws, Node.SIGNAL); 总结一下,Node的waitStatus 属性,初次设置是 在这个方法,也就是在此处; 是由其后继节点进行设置的!!!!!!~~~~~~~~~~~~ 为什么这样, 当前节点需要设置前任节点状态为 SIGNAL,确保它结束运行之后会来唤醒我,这样,当前线程才能够安心的去 park~~~ 需要切记的是,waitStatus 在AQS中被设置的地方非常少,通篇查找了下,发现仅有三个地方: 1 构造器3, 这个构造器仅用作于条件等待; 2 cancelAcquire 取消获取的时候,被调用6次 3 compareAndSetWaitStatus 被调用了8此,包括当前方法;主要是获取、释放、取消、转移 方法 4 fullyRelease 在5个类似的await 方法中被调用;表示已经完全的是否资源的占用; } return false; } /** * Convenience method to interrupt current thread. */ static void selfInterrupt() { Thread.currentThread().interrupt(); } /** * Convenience method to park and then check if interrupted * * @return {@code true} if interrupted */ private final boolean parkAndCheckInterrupt() { LockSupport.park(this); 通常只有两种途径可以唤醒该线程:1)被unpark();2)被interrupt() ,参考java.util.concurrent.locks.LockSupport#park(java.lang.Object),其实还有一种情况,是 spuriously 返回,也就是无法解释的现象? 被unpark 通常意味着前一个线程主动唤醒了当前线程,自己即将进入跳出等待状态,将再次tryAcquire,但是如果是被interrupt,那么 acquireQueued方法的循环仍然让线程 进入这个方法,进行自行阻塞; 那么什么情况下 线程会被 中断呢??? 需要理解一些线程的中断机制 return Thread.interrupted(); // 被唤醒之后,返回线程的中断状态。 } /* * 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,从独占、共享到控制模式(什么是控制模式) 他们都差不太多,但是又非常不同。只可能有 a little bit of factoring一点点分解(?),仅当和异常处理机制和其他的控制,基本上不会对性能造成太大的损失。 */ /** * Acquires in exclusive uninterruptible mode for thread already in * queue. Used by condition wait methods as well as acquire. * 为依据处于队列中的线程,通过独占不可中断的模式 获取锁; 用于条件等待或者普通的获取。 * @param node the node 此时的node 已经入队 * @param arg the acquire argument 需要获取的资源量(非常抽象的概念) * @return {@code true} if interrupted while waiting 返回true,表示等待过程中被中断过,否则返回false */ final boolean acquireQueued(final Node node, int arg) { 为什么需要对已经入队的node进行再次尝试呢?为什么不是直接进入等待状态呢(因为前面的tryAcquire 已经失败了啊)? boolean failed = true; try { boolean interrupted = false; for (;;) { 这个写法是所谓的自旋,为什么这里需要一个无限的for循环?? 需要考虑被唤醒,但是又获取资源失败的情况,这种情况下,需要再次进入等待状态。。如同我们的synchronized 中的条件wait,这样的wait方法也是需要使用一个while 来修饰条件的;当wait状态的线程被唤醒后需要再次判断条件,这里也是一个道理;否则会有逻辑上的错误!! final Node p = node.predecessor(); 获取前任node,这里为什么采用方法而不是prev指针呢? 因为predecessor会确保prev不为空,如果为空那么抛空指针异常,为什么这样做呢? 因为我们需要调用prev的方法, 不能调用的时候才出现空指针异常,需要尽早的抛出。 prev什么情况会是null,当它被取消、超时的情况下吧 if (p == head && tryAcquire(arg)) { 再次 调用子类实现的方法尝试性的获取资源!为什么这里需要再一次的尝试? 这里是理解难点。仅仅为了保险起见??? 还是说必不可少的??? 这里的if 可不可以放到下面的if 后面呢? 假设,当前线程是队列的第二个线程,首次tryAcquire失败,那么可能的原因只有是资源不够用,那么就入队,入队之后准备阻塞,那么它的前任如果是head 的话,而且如果head 是初始化CLH队列时候的那个正在执行的线程,即head是第一个线程,那么它其实就是new Node(),它的 waitStatus是0,那么假设它这个时候(执行到if这一行的这个时候)刚好执行完毕,那么它是不会对后继节点进行主动去唤醒的。那么假设我们没有当前这个 try,那么执行下面的if 会设置head的 waitStatus为SIGNAL,然后进入等待状态,但是问题是即使设置了,也于事无补, 前任节点是head 才有资格进行tryAcquire,这个比较好理解,它避免了各种无序的竞争 特别注意, 下面的两步,其实是重新设置了头,然后将旧的头出队~~.... AQS代码中不存在dequeue方法, 这里算是吧。 setHead(node); 万一获取成功了呢? 那么 就不要进入真正的等待状态了~~ p.next = null; // help GC failed = false; return interrupted; 这里的return 是这个方法的唯一出口 } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) 如果 shouldPark 为true,那么就park吧 interrupted = true; } } finally { if (failed) // 如果 tryAcquire始终是失败,而且for无限循环都结束了,那么 failed,而且肯定发生了什么异常,那么就取消获取吧; cancelAcquire(node); } } /** * 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(); 如果被中断,那么就立即抛异常,不同于上面的acquireQueued 仅仅是标记一下; } } finally { if (failed) cancelAcquire(node); } } /** * Acquires in exclusive timed mode. 独占模式 * * @param arg the acquire argument * @param nanosTimeout max wait time nanos纳米 这里应该是指nanoseconds 纳秒 * @return {@code true} if acquired 如果nanosTimeout时间内获取到了,那么表示成功,返回true;没超时则等待;超时了则返回false */ private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { if (nanosTimeout <= 0L) return false; nanosTimeout <= 0L 那么就永远别获取成功,因为已经或立即超时,没有机会去获取资源 final long deadline = System.nanoTime() + nanosTimeout; final Node node = addWaiter(Node.EXCLUSIVE); boolean failed = true; 执行上面的3行代码(下面也还有这样的代码),也是需要时间的,虽然可能是非常非常少的时间,但是也是肯定多多少少需要一点时间的。但是nanosTimeout 不考虑这些时间 ; 因为实在没法做到理论上 100%的严格精确; ———— 即使精确到纳秒,其实也只能做到逻辑上精确; 理论上的做不到; ———— 再怎么高新尖端的科技 也会遇到这样那样的瓶颈~~!! 怎么说呢,刚接触这个AQS的时候,总会有各种疑惑;需要动脑去理解。 try { for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { 先还是进行一次的尝试获取 setHead(node); p.next = null; // help GC failed = false; return true; } nanosTimeout = deadline - System.nanoTime(); 重新计算nanosTimeout, if (nanosTimeout <= 0L) return false; 已经没时间了,那么无法获取资源,就返回false吧 唤醒也好,超时也好,到这里如果发现没时间了,那么 直接返回false。 不再尝试去获取资源!! if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold) 如果超时时间 > 自旋时间,那么阻塞;否则就自旋吧 shouldParkAfterFailedAcquire: 检查并更新前任的状态, LockSupport.parkNanos(this, nanosTimeout); 那么把线程阻塞个 nanosTimeout的时间 if (Thread.interrupted()) throw new InterruptedException(); 如果被中断,直接抛异常, 可见这个方法也是 中断敏感的。 parkNanos的时候不会响应中断,但是完了之后会进行一次是否中断的检查 } } finally { if (failed) cancelAcquire(node); } } /** * 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 (;;) { final Node p = node.predecessor(); if (p == head) { 前任是head,再一次尝试tryAcquireShared int r = tryAcquireShared(arg); if (r >= 0) { >= 就表示获取成功了,那么设置head为当前线程node,并且如果有剩余,尝试传播扩散 自己得到资源后,如果还有剩余,还要顺序的往后面的节点 有序的进行广播 setHeadAndPropagate(node, r); p.next = null; // help GC if (interrupted) selfInterrupt(); 补中断 failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } } /** * Acquires in shared interruptible mode. 共享模式 * @param arg the acquire argument */ private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); 及时响应中断,做法就是简单的抛出,让子类去处理中断 } } finally { if (failed) cancelAcquire(node); } } /** * Acquires in shared timed mode. 共享模式下获取资源,忽略中断。 * * @param arg the acquire argument * @param nanosTimeout max wait time * @return {@code true} if acquired */ private boolean doAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException { if (nanosTimeout <= 0L) return false; final long deadline = System.nanoTime() + nanosTimeout; final Node node = addWaiter(Node.SHARED); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); 自己得到资源后,如果还有剩余,还要顺序的往后面的节点 有序的进行广播 特别注意, 下面的两步,其实是重新设置了头,然后将旧的头出队~~.... AQS代码中不存在dequeue方法, 这里算是吧。 p.next = null; // help GC failed = false; return true; } } nanosTimeout = deadline - System.nanoTime(); if (nanosTimeout <= 0L) return false; if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold) LockSupport.parkNanos(this, nanosTimeout); if (Thread.interrupted()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } } // Main exported methods /** * 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. 独占模式下尝试获取资源。这个方法应该首先查询看当前state值是否允许,资源足够则获取 * *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的线程执行,如果获取失败了,那么把线程入队(如果还没入队),直到其他线程调用release方法发出了信号释放; * *
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. * *This method is always invoked by the thread performing release. * *
The default implementation throws * {@link UnsupportedOperationException}. 尝试去设置state,以反映独占锁的释放; 这个方法永远都是只会被 正在执行release的方法调用。 正常来说,tryRelease()都会成功的,因为这是独占模式,该线程来释放资源,那么它肯定已经拿到独占资源了,直接减掉相应量的资源即可(state-=arg),也不需要考虑线程安全的问题。但要注意它的返回值,上面已经提到了,release()是根据tryRelease()的返回值来判断该线程是否已经完成释放掉资源了!所以自义定同步器在实现时,如果已经彻底释放资源(state=0),要返回true,否则返回false。 一定要 保证彻底释放了资源,即state==0 才返回true,否则呢 可能引起 一些混乱。 链接:
https://www.jianshu.com/p/da9d051dcc3d * * @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. arg 指明释放的资源的量,或者当前AQS的正在处于一个条件等待状态的线程的node的state的值,否则呢就不做任何的解释,所以它也可以是任何你喜欢的值;(保证逻辑正确即可) * @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. 如果已经完全的释放,那么就返回true,从而呢,其他的等待状态的线程得以尝试去获取资源;否则抛异常 * @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. 如果AQS状态错误, * @throws UnsupportedOperationException if exclusive mode is not supported */ protected boolean tryRelease(int arg) { throw new UnsupportedOperationException(); } /** * 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. 共享模式下获取资源,这个方法应该首先查询看资源量是否足够.. * *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. * *
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. 如果获取失败返回负数,如果获取成功但没有其他资源(即其他的线程无法再成功获取)返回0,如果获取成功且还有其他资源剩余返回正数————此时,直接后继的那个等待状态的node应该坚持是否可用性;这个方法有时可以用于独占模式.? * @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. a consistent fashion 一致的方式,相同的样式 * @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. * *This method is always invoked by the thread performing release. 尝试设置state,以反映共享模式下的release释放,使得等待的线程得以获取资源。。 * *
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 如果这个共享模式的release方法 可能允许一个 等待状态的acquire方法执行成功(可以理解为tryAcquire 同资源量的线程能够成功,)那么返回true,否则就false * @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(); } /** * Returns {@code true} if synchronization is held exclusively with * respect to the current (calling) thread. This method is invoked * upon each call to a non-waiting {@link ConditionObject} method. * (Waiting methods instead invoke {@link #release}.) 是否被独占, 如果同步性被当前线程独占了,那么返回true,否则就false。每次调用非等待的ConditionObject方法的时候都会执行这个方法。而等待的方法会执行release * *The default implementation throws {@link * UnsupportedOperationException}. This method is invoked * internally only within {@link ConditionObject} methods, so need * not be defined if conditions are not used. 这个方法仅会被用于 ConditionObject 方法的内部执行(?) 所以呢,如果没有用到条件同步机制,这个方法可以不用实现。 评: 其实Node 内提供有一个判断是共享模式 还是独占模式的 方法,和这个差不多吧。 * * @return {@code true} if synchronization is held exclusively; * {@code false} otherwise * @throws UnsupportedOperationException if conditions are not supported
*/ protected boolean isHeldExclusively() { throw new UnsupportedOperationException(); } /** * 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}. 独占模式下获取资源,忽略中断,至少调用一次tryAcquire,然后反复的...好像之前分析过? * * @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)) selfInterrupt(); 如果线程在等待过程中被中断过,它是不响应的。只是在此处,获取资源后才再进行自我中断selfInterrupt(),将中断补上 执行到这里,acquire方法正式结束,获取资源成功,首次进入到lock方法的内部,执行业务代码!!! 理解这点很重要!!! } /** * Acquires in exclusive mode, aborting if interrupted. 尝试,如果被打断,那么就放弃并抛异常 * Implemented by first checking interrupt status, then invoking * at least once {@link #tryAcquire}, returning on * success. Otherwise the thread is queued, possibly repeatedly * blocking and unblocking, invoking {@link #tryAcquire} * until success or the thread is interrupted. This method can be * used to implement method {@link Lock#lockInterruptibly}. * * @param arg the acquire argument. This value is conveyed to * {@link #tryAcquire} but is otherwise uninterpreted and * can represent anything you like. * @throws InterruptedException if the current thread is interrupted */ 这是一个public, 本类中没有被调用,一般由子类调用以实现各种功能。。 public final void acquireInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (!tryAcquire(arg)) doAcquireInterruptibly(arg); } /** * Attempts to acquire in exclusive mode, aborting if interrupted, * and failing if the given timeout elapses. Implemented by first * checking interrupt status, then invoking at least once {@link * #tryAcquire}, returning on success. Otherwise, the thread is * queued, possibly repeatedly blocking and unblocking, invoking * {@link #tryAcquire} until success or the thread is interrupted * or the timeout elapses. This method can be used to implement * method {@link Lock#tryLock(long, TimeUnit)}. 独占模式下获取资源,如果被打断,那么中止;如果此时,那么失败;至少调用一次tryAcquire,然后入队、可能会反复的阻塞-解阻塞,直到成功返回,或者就是超时、中断等失败; * * @param arg the acquire argument. This value is conveyed to * {@link #tryAcquire} but is otherwise uninterpreted and * can represent anything you like. * @param nanosTimeout the maximum number of nanoseconds to wait * @return {@code true} if acquired; {@code false} if timed out * @throws InterruptedException if the current thread is interrupted */ public final boolean tryAcquireNanos(int arg, long nanosTimeout) 单位是纳秒 throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); return tryAcquire(arg) || doAcquireNanos(arg, nanosTimeout); } /** * 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}. 独占模式下释放资源: 如果尝试释放tryRelease成功了,那么 解锁一个或多个线程;什么情况会是多个? * * @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) { if (tryRelease(arg)) { 先尝试释放 Node h = head; 仅从头结点开始进行释放 if (h != null && h.waitStatus != 0) 如果状态不满足,说明那么就不唤醒后继了,直接返回true; 为什么需要这么多的判断, 因为实际情况可能很复杂, AQS作为关键的基础实现,需要考虑各种情况 如果head的waitStatus不为0,那么尝试唤醒后继;为什么不细分呢,如果waitStatus是1呢? 哦, 有可能为1,如果是CONDITION呢? 哦,也不可能为CONDITION, 仅可能是CANCEL/SIGNAL或者PROPAGATE unparkSuccessor(h); return true; } return false; } /** * 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. 共享模式下获取资源,忽略中断。实现方式: 首先调用至少一次tryAcquireShared,成功则直接返回,否则把线程入队,可能会反复的阻塞、唤醒、调用tryAcquireShared,直到成功。 ———— 忽略了中断, doAcquireShared方法内进行了事后补偿调用 * * @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); } /** * Acquires in shared mode, aborting if interrupted. Implemented * by first checking interrupt status, then invoking at least once * {@link #tryAcquireShared}, returning on success. Otherwise the * thread is queued, possibly repeatedly blocking and unblocking, * invoking {@link #tryAcquireShared} until success or the thread * is interrupted. * @param arg the acquire argument. * This value is conveyed to {@link #tryAcquireShared} but is * otherwise uninterpreted and can represent anything * you like. * @throws InterruptedException if the current thread is interrupted */ public final void acquireSharedInterruptibly(int arg) 共享模式下获取资源,响应中断。 throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg); } /** * Attempts to acquire in shared mode, aborting if interrupted, and * failing if the given timeout elapses. Implemented by first * checking interrupt status, then invoking at least once {@link * #tryAcquireShared}, returning on success. Otherwise, the * thread is queued, possibly repeatedly blocking and unblocking, * invoking {@link #tryAcquireShared} until success or the thread * is interrupted or the timeout elapses. * * @param arg the acquire argument. This value is conveyed to * {@link #tryAcquireShared} but is otherwise uninterpreted * and can represent anything you like. * @param nanosTimeout the maximum number of nanoseconds to wait * @return {@code true} if acquired; {@code false} if timed out * @throws InterruptedException if the current thread is interrupted */ 共享模式下获取资源,响应超时和中断。 public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); return tryAcquireShared(arg) >= 0 || doAcquireSharedNanos(arg, nanosTimeout); } /** * Releases in shared mode. Implemented by unblocking one or more * threads if {@link #tryReleaseShared} returns true. 共享模式下释放,实现方式: 尝试去调用tryReleaseShared,如果成功则解阻塞一个或多个线程 * * @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)) { 释放掉资源后,唤醒后继。跟独占模式下的release()相似,但有一点稍微需要注意:独占模式下的tryRelease()在完全释放掉资源(state=0)后,才会返回true去唤醒其他线程,这主要是基于独占下可重入的考量;而共享模式下的releaseShared()则没有这种要求,共享模式实质就是控制一定量的线程并发执行,那么拥有资源的线程在释放掉部分资源时就可以唤醒后继等待结点。 作者:圈圈_Master 链接:https://www.jianshu.com/p/da9d051dcc3d doReleaseShared(); return true; } return false; } // Queue inspection methods /** * Queries whether any threads are waiting to acquire. Note that * because cancellations due to interrupts and timeouts may occur * at any time, a {@code true} return does not guarantee that any * other thread will ever acquire. 查询是否还有任何的线程处于等待状态。 因为中断和超时可能在不确定是时间发生,那么这里返回true并不保证其他线程将会继续尝试获取。(?)返回true表明还有等待的线程,那么如果线程acquire成功,那么可能队列应该为空,此时又应该返回false。那么确实可能不太准确。 更新:这样解释吧:: 比如调用此方法的时候是true,然而如果接下来很快就发生中断和超时,那么就会有其他线程被唤醒,然后它出队,然后队列可能变为空,然后返回值 是不是为false呢? 是的,但是这个已经没有更好办法处理了, 只能说无法保证。 * *In this implementation, this operation returns in * constant time. 仅仅是做一个比较,因此可以在非常短的时间内返回。 * * @return {@code true} if there may be other threads waiting to acquire 只要还有其他的线程处于等待获取资源状态, 那么返回true
*/ public final boolean hasQueuedThreads() { return head != tail; } /** * Queries whether any threads have ever contended to acquire this * synchronizer; that is if an acquire method has ever blocked. 查询是否曾经有任何的线程 竞争获取 同步器的情况出现,也就是说判断是否曾经有acquire方法被阻塞过 * *In this implementation, this operation returns in * constant time. 仅仅是做一个比较,因此可以在非常短的时间内返回。 * * @return {@code true} if there has ever been contention
*/ public final boolean hasContended() { return head != null; 很简单,只需要判断是否被初始化过一次即可。初始化过后, head会保持不为空,即使后面所有的线程全部出队了 } /** * Returns the first (longest-waiting) thread in the queue, or * {@code null} if no threads are currently queued. 获取队列中的第一个线程,也就是等待最久的那个,或者如果目前没有任何线程入队(可能都已经出队或者未被初始化)则返回null * *In this implementation, this operation normally returns in * constant time, but may iterate upon contention if other threads are * concurrently modifying the queue. 仅仅是做一个比较,因此可以在非常短的时间内返回;但是如果其他线程目前正在修改队列,那么也可能需要进行轮训 * * @return the first (longest-waiting) thread in the queue, or * {@code null} if no threads are currently queued
*/ public final Thread getFirstQueuedThread() { // handle only fast path, else relay return (head == tail) ? null : fullGetFirstQueuedThread(); } /** * Version of getFirstQueuedThread called when fastpath fails */ private Thread fullGetFirstQueuedThread() { /* * The first node is normally head.next. Try to get its * thread field, ensuring consistent reads: If thread * field is nulled out or s.prev is no longer head, then * some other thread(s) concurrently performed setHead in * between some of our reads. We try this twice before * resorting to traversal. 第一个node通常就是head.next,需要保证读操作的一致性;因为可能我们的数次的读操作的过程中,head可能已经被重新设置; */ Node h, s; Thread st; if (((h = head) != null && (s = h.next) != null && s.prev == head && (st = s.thread) != null) || ((h = head) != null && (s = h.next) != null && s.prev == head && (st = s.thread) != null)) 比较难理解啊 return st; /* * Head's next field might not have been set yet, or may have * been unset after setHead. So we must check to see if tail * is actually first node. If not, we continue on, safely * traversing from tail back to head to find first, * guaranteeing termination. Head的 next字段可能已经被置位null,或者被unset,或者还未设置(还未初始化?),所以我们需要检查 tail 是不是就是FirstQueuedThread;如果不是,我们反向遍历(安全起见)然后找到First Queued Thread */ Node t = tail; Thread firstThread = null; while (t != null && t != head) { Thread tt = t.thread; if (tt != null) firstThread = tt; t = t.prev; } return firstThread; } /** * Returns true if the given thread is currently queued. 判断给定的线程是否已经入队 * *This implementation traverses the queue to determine * presence of the given thread. 反向遍历队列,找到相等,找到了就返回true,否则就false * * @param thread the thread * @return {@code true} if the given thread is on the queue * @throws NullPointerException if the thread is null
*/ public final boolean isQueued(Thread thread) { if (thread == null) throw new NullPointerException(); 不允许为空 for (Node p = tail; p != null; p = p.prev) if (p.thread == thread) return true; return false; } /** * Returns {@code true} if the apparent first queued thread, if one * exists, is waiting in exclusive mode. If this method returns * {@code true}, and the current thread is attempting to acquire in * shared mode (that is, this method is invoked from {@link * #tryAcquireShared}) then it is guaranteed that the current thread * is not the first queued thread. Used only as a heuristic in * ReentrantReadWriteLock. 判断apparent first queued thread即看起来是第一个入队的线程,是否独占模式。 如果返回true,而且当前线程正在尝试共享模式获取同步器(通过tryAcquireShared方法调用),那么可以保证的是当前线程不是first queued thread, ———— 比较难理解啊, 如果返回true那么却又不是first queued thread,那么不是应该返回false? 这个方法仅会作为探索法 被用于ReentrantReadWriteLock ———— 为什么不把它移到ReentrantReadWriteLock去实现? heuristic 启发法;启发性;探索法 */ final boolean apparentlyFirstQueuedIsExclusive() { Node h, s; return (h = head) != null && (s = h.next) != null && !s.isShared() && s.thread != null; } /** * Queries whether any threads have been waiting to acquire longer * than the current thread. 查询是否存在比当前线程等待更久的线程 ———— 我感觉这句话的描述是不准确的,误导性的;实际上,这个方法的作用 仅仅是判断当前线程的node是否为第2个node, * *An invocation of this method is equivalent to (but may be * more efficient than): *
{@code * getFirstQueuedThread() != Thread.currentThread() && * hasQueuedThreads()}* *
Note that because cancellations due to interrupts and * timeouts may occur at any time, a {@code true} return does not * guarantee that some other thread will acquire before the current * thread. Likewise, it is possible for another thread to win a * race to enqueue after this method has returned {@code false}, * due to the queue being empty. 类似地 如果这个方法返回false,其他的线程是可能赢得竞争然后入队的,因为队列为空; ————???为什么队列为空 更新: 看代码可知,如果返回false,那就是说 返回false,意味着当前线程是head、正在执行或者它就是head.next * *
This method is designed to be used by a fair synchronizer to * avoid barging. barging 这里理解为闯进来 * Such a synchronizer's {@link #tryAcquire} method should return * {@code false}, and its {@link #tryAcquireShared} method should * return a negative value, if this method returns {@code true} * (unless this is a reentrant acquire). For example, the {@code * tryAcquire} method for a fair, reentrant, exclusive mode * synchronizer might look like this: 这个方法被设计用于公平同步器,以避免AbstractQueuedSynchronizer的barging 方法。如果这个方法返回true, 那么这样的公平同步器的tryAcquire应该返回false同时它的tryAcquireShared返回负数; (除非当前方法是可重入的acquire )。举个例子,公平的、可重入的独占模式的同步器 的tryAcquire 方法示例如下: * *
{@code * protected boolean tryAcquire(int arg) { * if (isHeldExclusively()) { * // A reentrant acquire; increment hold count * return true; * } else if (hasQueuedPredecessors()) { * return false; * } else { * // try to acquire normally * } * }}* * @return {@code true} if there is a queued thread preceding the * current thread, and {@code false} if the current thread * is at the head of the queue or the queue is empty 如果 队列中,当前线程的前面还有一个线程 (也就是第二个?) 返回true 如果 当前线程是head即位于队列的头或者队列为空 返回false * @since 1.7 */ 顾名思义,这个方法是判断是否有已经入队的前任;即判断是不是第二个node。 public final boolean hasQueuedPredecessors() { // The correctness of this depends on head being initialized // before tail and on head.next being accurate if the current // thread is first in queue. 如果当前线程就是队列的第一个node,那么 这个方法是否正确 依赖于 head是否已经被初始化过 和 head.next 是否准确 ———— 这个语句好难翻译理解.. Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; return h != t && // h != t 首先要求 队列至少有两个node ((s = h.next) == null || s.thread != Thread.currentThread()); 并且第二个节点为null 或者 第二个节点对应的线程不是当前正在执行的线程, 那么返回true 这个判断,看不太懂,当前线程不是h.next 就能够表示当前线程的前面还有前任吗?不完全是吧,如果当前线程是第三个,同时也有前任,那么满足这个条件,这没错;但是如果当前线程是head呢? 那么也是可以满足这个判断的(比如此时有 >2 个node),但是呢,head前面肯定是没有node的,那么就不满足了吧.. 查看了下,这个方法的调用场景 基本上仅仅是用于 子类的tryAcquire或者tryAcquireShared 中, } // Instrumentation and monitoring methods /** * Returns an estimate of the number of threads waiting to * acquire. The value is only an estimate because the number of * threads may change dynamically while this method traverses * internal data structures. This method is designed for use in * monitoring system state, not for synchronization * control. 获取同步队列的长度,即队列中node的个数,注意,这里仅仅返回一个大概的数字。因为方法遍历执行过程中node可能发生变化;这个方法用于监视系统的state(所谓系统state,应该是 what? )而不是同步器的控制。 * * @return the estimated number of threads waiting to acquire */ public final int getQueueLength() { int n = 0; for (Node p = tail; p != null; p = p.prev) { 反向遍历 if (p.thread != null) ++n; 只要node对应的线程不为空,那么算一个; head呢?head的thread为null,但head难道不算? ———— 可以理解为不包括头结点,仅仅返回正在等待之中的节点。head已经处于运行之中,故不算.. } return n; } /** * Returns a collection containing threads that may be waiting to * acquire. Because the actual set of threads may change * dynamically while constructing this result, the returned * collection is only a best-effort estimate. The elements of the * returned collection are in no particular order. This method is * designed to facilitate construction of subclasses that provide * more extensive monitoring facilities. 返回同步队列中的等待状态的线程的集合;同上,仅仅返回一个 best-effort的大概的状况。集合没有特别的顺序(? 应该就是说保持原来的顺序), 这个方法被设计用于给子类的构造提供便利。 * * @return the collection of threads */ public final Collection
Method documentation for this class describes mechanics, * not behavioral specifications from the point of view of Lock * and Condition users. Exported versions of this class will in * general need to be accompanied by documentation describing * condition semantics that rely on those of the associated * {@code AbstractQueuedSynchronizer}. 这个类的方法文档描述了具体的机制,而不是 Lock和Condition用户试点出发的 行为规范xxx 这个类的出口版本,通常也需要描述条件语义的文档 伴随 * *
This class is Serializable, but all fields are transient, * so deserialized conditions have no waiters. 虽然是Serializable,但是所有字段都是“瞬态”(即不做持久化),所以被反序列化后,它不会有等待者;(因为序列化的时候无法把等待者保留下来啊。。) 等待队列到底长什么样呢? nextWaiter nextWaiter nextWaiter nextWaiter firstWaiter ----------> waiter1 ----------> waiter2 ----------> waiter3 ----------> lastWaiter
*/ public class ConditionObject implements Condition, java.io.Serializable { private static final long serialVersionUID = 1173984872572414699L; /** First node of condition queue. */ private transient Node firstWaiter; 条件队列中的首个的等待者, 也是Node类型。 /** Last node of condition queue. */ private transient Node lastWaiter; 条件队列中的末尾的等待者, 也是Node类型。 /** * Creates a new {@code ConditionObject} instance. */ public ConditionObject() { } // Internal methods /** * Adds a new waiter to wait queue. 把新进来的等待者添加到条件等待队列 wait queue 和 condition queue 是不是同一个含义?? 为什么这么多的概念?? * @return its new wait node */ private Node addConditionWaiter() { Node t = lastWaiter; 获取末尾的等待者 // If lastWaiter is cancelled, clean out. 如果末尾的等待者已经被取消,那么删除它 if (t != null && t.waitStatus != Node.CONDITION) { 这里为什么是判断 不等于CONDITION; 因为waitStatus仅有5个值,而条件队列中只有2个值, 除了 CANCEL 就是 CONDITION..xxx 那么为什么不直接的 判断是否等于CANCEL呢?xxx unlinkCancelledWaiters(); unlink 可以理解为删除的意思 t = lastWaiter; 重新读取 末尾的等待者 } Node node = new Node(Thread.currentThread(), Node.CONDITION); 使用当前线程构建一个新的Node if (t == null) 如果 末尾的等待者为空, firstWaiter = node; 说明还未初始化,那么初始化一下,其实就是把first设置一下即可 else t.nextWaiter = node; 否则使用nextWaiter指针 把它关联/添加到尾巴的后面,作为新的尾巴。 lastWaiter = node; 重新设置尾巴。 return node; } /** * Removes and transfers nodes until hit non-cancelled one or * null. Split out from signal in part to encourage compilers 这一句好难理解.. * to inline the case of no waiters. 移除并转移node 到同步队列,如果node已经被取消或者为null,那么循环,直到遇到未被取消的或者遇到了null ; 遇到了则意味着,它被取消了 那么让它出队。 把doSignal和signal 两个方法拆开(也许本来是可以只使用一个方法的),部分原因是为了编译器更好的把node没有等待者的情况 内联起来?xxx 具体如何内联? Split out 分散、拆分、 in part to 部分地 * @param first (non-null) the first node on condition queue 位于条件队列中的第一个。其实感觉这个参数可以不用传递,我们直接当前firstWaiter,不可以吗? 为什么一定要找到一个未被取消的node来进行通知? 因为取消的node 可能仍然存在于条件队列;对它进行通知, 是没有意义的,因为它已经位于了同步队列。 特别注意: signal在这里还不是唤醒的意思,而仅仅是 让它入同步的queue,就是说通知它,让它进入同步队列 等待!~ 就是让它结束在条件上的等待! 就是说通知它现在条件以及满足了,你继续执行吧! */ doSignal的do while 循环,第一步就是把first从条件队列中移除,并设置新的firstWaiter private void doSignal(Node first) { 首先判断一下参数first的是不是第一个,如果是,特殊处理一下。 然后设置nextWaiter指针为空—————— 其实就是准备是要把它移出条件队列,转移到同步队列 然后执行真正的转移,这个转移是在while的 条件判断中完成的, 同时会利用这个转移的结果,如果转移成功,那么循环结束、方法也结束; 如果失败了,那就把first设置为 第一个,判断,然后继续循环.. do { if ( (firstWaiter = first.nextWaiter) == null) 把firstWaiter设置为参数first的下一个等待者,如果它为空,说明它没有等待的线程,那么把末尾的等待者 也设置为空。 lastWaiter = null; 执行到这里, 说明transferForSignal失败或者firstWaiter为空 ,一般是状态不对,那么把first的后继设置为空,相当于让first出队,缩短了条件队列 first.nextWaiter = null; } while (!transferForSignal(first) && 如果转移first失败———— 通常是由于first可能已经被取消 (first = firstWaiter) != null); 而且firstWaiter还不为空,那么继续循环,firstWaiter为空说明 整个条件队列都已经为空。 不管是不是被取消, 总而言之,这个方法保证会把一个不为空的头出发的一个node转移到同步队列,让它 有机会得到唤醒。———— 只要确保了它将会得到唤醒,那么就不会出什么错...? 我大概是这样理解的.. 因为transferForSignal 会设置它的参数的前任的状态为SIGNAL.. } /** * Removes and transfers all nodes. 移除并转移所有的条件队列上的node * @param first (non-null) the first node on condition queue */ private void doSignalAll(Node first) { lastWaiter = firstWaiter = null; do { Node next = first.nextWaiter; 从头开始 first.nextWaiter = null; nextWaiter字段要全部废弃 transferForSignal(first); 转移 first = next; 顺藤摸瓜下一个 } while (first != null); 直到不为空,为空则继续循环.. } /** * Unlinks cancelled waiter nodes from condition queue. * Called only while holding lock. This is called when * cancellation occurred during condition wait, and upon * insertion of a new waiter when lastWaiter is seen to have * been cancelled. This method is needed to avoid garbage * retention in the absence of signals. So even though it may * require a full traversal, it comes into play only when * timeouts or cancellations occur in the absence of * signals. It traverses all nodes rather than stopping at a * particular target to unlink all pointers to garbage nodes * without requiring many re-traversals during cancellation * storms. 从条件队列中删除已经被取消的等待者,仅仅在持有锁的情况下被调用。出现在条件等待的时候取消操作发生 和 lastWaiter已经被取消的时候等待者的新增操作 这个方法需要避免 缺乏信号导致的 gc 保留。所以呢,可能需要一个完全的遍历, 仅仅在 缺乏信号导致的超时或取消操作时起作用。 可以避免取消的风暴?? 好TM难理解。———— 其实看源码还比较好理解.. unlinkCancelledWaiters 说白了就是绕过已经取消了的node,重新构建条件队列。 */ private void unlinkCancelledWaiters() { 注意这个方法末尾的单词是负数, 就是说会把 所有的 已经取消的等待者 都处理一遍(就是干掉) Node t = firstWaiter; 从头开始 Node trail = null; while (t != null) { Node next = t.nextWaiter; if (t.waitStatus != Node.CONDITION) { node位于条件队列,但状态不是CONDITION,那么肯定是CANCEL t.nextWaiter = null; 不管三七二十一,先把nextWaiter设置为空 if (trail == null) firstWaiter = next; 这个时候,只有头,既然已经是CANCEL,那么把它干掉 怎么做的呢? 其实就是把firstWaiter指向了元firstWaiter的next,相当于bypass了原来的firstWaiter! else trail.nextWaiter = next; 把没有被取消的那个node的nextWaiter指针指向next,相当于bypass了当前的t! if (next == null) lastWaiter = trail; 如果next为空,那么已经到了末尾了,重新设置尾巴。 } else trail = t; trail 其实是没有被取消的那个node t = next; } } // public methods /** * Moves the longest-waiting thread, if one exists, from the * wait queue for this condition to the wait queue for the * owning lock. 把等待最久的线程从条件队列中转移到 等待队列中; 所谓的wait queue等待队列,其实就是sync queue同步队列!!!~~ * * @throws IllegalMonitorStateException if {@link #isHeldExclusively} * returns {@code false} */ public final void signal() { if (!isHeldExclusively()) 同步器是不是被当前线程独占? throw new IllegalMonitorStateException(); Node first = firstWaiter; 获取第一个 if (first != null) doSignal(first); 发出信号通知它; 只有当前线程已经拥有了AQS 锁,才有资格去唤醒其他线程,否则.. } /** * Moves all threads from the wait queue for this condition to * the wait queue for the owning lock. 把所有的线程从条件队列中转移到 等待队列中 * * @throws IllegalMonitorStateException if {@link #isHeldExclusively} * returns {@code false} */ public final void signalAll() { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); Node first = firstWaiter; if (first != null) doSignalAll(first); } /** * Implements uninterruptible condition wait. *-
*
- Save lock state returned by {@link #getState}. *
- Invoke {@link #release} with saved state as argument, * throwing IllegalMonitorStateException if it fails. *
- Block until signalled. *
- Reacquire by invoking specialized version of * {@link #acquire} with saved state as argument. *
-
*
- If current thread is interrupted, throw InterruptedException. *
- Save lock state returned by {@link #getState}. *
- Invoke {@link #release} with saved state as argument, * throwing IllegalMonitorStateException if it fails. *
- Block until signalled or interrupted. *
- Reacquire by invoking specialized version of * {@link #acquire} with saved state as argument. *
- If interrupted while blocked in step 4, throw InterruptedException. *
-
*
- If current thread is interrupted, throw InterruptedException. *
- Save lock state returned by {@link #getState}. *
- Invoke {@link #release} with saved state as argument, * throwing IllegalMonitorStateException if it fails. *
- Block until signalled, interrupted, or timed out. 阻塞,除非被唤醒、打断、超时 *
- Reacquire by invoking specialized version of 重新读取资源; specialized version就是指acquireQueued,独占模式获取 * {@link #acquire} with saved state as argument. *
- If interrupted while blocked in step 4, throw InterruptedException. 如果被打断,也要抛异常! *
-
*
- If current thread is interrupted, throw InterruptedException. *
- Save lock state returned by {@link #getState}. *
- Invoke {@link #release} with saved state as argument, * throwing IllegalMonitorStateException if it fails. *
- Block until signalled, interrupted, or timed out. *
- Reacquire by invoking specialized version of * {@link #acquire} with saved state as argument. *
- If interrupted while blocked in step 4, throw InterruptedException. *
- If timed out while blocked in step 4, return false, else true. *
-
*
- If current thread is interrupted, throw InterruptedException. *
- Save lock state returned by {@link #getState}. *
- Invoke {@link #release} with saved state as argument, * throwing IllegalMonitorStateException if it fails. *
- Block until signalled, interrupted, or timed out. *
- Reacquire by invoking specialized version of * {@link #acquire} with saved state as argument. *
- If interrupted while blocked in step 4, throw InterruptedException. *
- If timed out while blocked in step 4, return false, else true. *