首先我们需要理解一些基本的概念,然后我们对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 ———— 最好还是理解为 同步锁
下面是 源码级别 逐字逐句 详尽分析!!!
/**
提供了一个实施封锁锁和相关的框架。
同步器(semaphores),依赖
先入先出(FIFO)等待队列。 这个类的设计是为了
是大多数依赖同步器的基础。
单个原子{@code int}值来表示状态。子类
必须定义改变这种状态的受保护方法,并且必须定义改变这种状态的
定义该状态对该对象的意义是什么?
或释放。 考虑到这些,这个类中的其他方法都携带了
出所有的排队和阻挡机制。子类可以保持
其他状态字段,但只有原子式更新的{@code int}。
使用方法{@link #getState}, {@link
#setState}和{@link #compareAndSetState}被追踪到的是关于
到同步。
<P>子类应定义为非公开的内部帮助程序。
类,用于实现同步属性。
其所附班级的。 类
{@codeAbstractQueuedSynchronizer}没有实现任何的
同步接口。 相反,它定义了一些方法,如
{@link #acquireInterruptibly},可以被调用为
具体的锁具和相关的同步器,以适当地
实现它们的公共方法。
<p>这个类支持默认的<em>排他性</em>,也支持默认的<em>排他性</em>。
模式和<em>共享</em>模式。在独占模式下获得时。
其他线程试图的获取不能成功。共享模式
通过多线程获取可能(但不需要)成功。该类
不理解这些差异,但在下列情况下除外:
机械地认为,当共享模式获得成功后,下一个
等待的线程(如果有的话)也必须确定它是否能够
获得以及。在不同模式下等待的线程共享
同一FIFO队列。通常情况下,实现子类只支持
这些模式中的一种,但这两种模式都可以发挥作用,例如在
{@link ReadWriteLock}。只支持排他性的子类或
仅共享模式不需要定义支持未使用模式的方法。
<p>这个类定义了一个嵌套的{@link条件对象}类,它是
子类可以作为{@link条件}的实现,通过子类
支持独占模式的方法{@link
#isHeldExclusively}报告同步是否是专门的
对当前线程持有的方法{@link #release}。
调用当前{@link #getState}的值时完全释放了
这个对象,以及{@link #acquire},给定这个保存的状态值。
最终将此对象恢复到之前的获得状态。 没有
{@code AbstractQueuedSynchronizer}方法,否则就会创建这样一个
条件,所以,如果不能满足这个约束,就不要使用它。 的
{@link条件对象}的行为当然取决于
其同步器实现的语义。
<p>该类提供了检测、仪表和监控功能。
内部队列的方法,以及类似的内部队列的方法。
条件对象。这些对象可以根据需要导出为类
使用{@code AbstractQueuedSynchronizer}为他们的
同步机理。
<p>该类的序列化只存储底层原子的
整数维持状态,所以反序列化对象具有空的
线程队列。需要序列化的典型子类将
定义一个{@code readObject}方法,将其还原为已知的
在反序列化时的初始状态。
<h3>使用</h3></h3>。
<p>要使用该类作为同步器的基础,请重新定义
通过检查和/或修改,酌情采用以下方法: 1.
同步状态使用{@link #getState}, {@link
#setState}和/或{@link #compareAndSetState}。
<ul
<li> {@link #tryAcquire}{@link #tryAcquire}
<li> {@link #tryRelease}{@link #tryRelease}
<li> {@link #tryAcquireShared}{@link #tryAcquireShared}
<li> {@link #tryReleaseShared}{@link #tryReleaseShared}
<li> {@link #isHeldExclusively}{@link #isHeldExclusively}
</ul>
这些方法中的每个方法默认都会抛出{@link
UnsupportedOperationException}。 这些方法的实现
必须是内部线程安全的,一般来说应该是短的,而且
而不阻塞。定义这些方法是<em>唯一支持的</em>。
使用这个类的方法。所有其他的方法都声明
{@code final},因为它们不能独立变化。
<p>你也可以从{@link
AbstractOwnableSynchronizer}有用的线程跟踪器。
拥有一个专属的同步器。 我们鼓励你使用它们
-- 这使得监测和诊断工具能够协助用户在以下方面进行监测和诊断:
确定哪些线程持有锁。
<p>尽管这个类是基于内部FIFO队列,但它
并不自动执行FIFO获取策略。 核心的
独占方式同步如下:
<pre>
Acquire。
while (!tryAcquire(arg)) {
<em>如果线程还没有被排队,那么就取消对线程的请求</em>。
<em>可能阻止当前线程</em>。
}
释放。
if (tryRelease(arg))
<em>解锁第一个排队的线程</em>。
</pre></pre>
共享模式类似,但可能涉及到级联信号)。
因为获取方法中的检查在入队之前,新获取的线程可以<em>barge</em>领先于
其他被阻止和排队的人。 不过,如果你愿意,可以。
定义{@code tryAcquire}和/或{@code tryAcquireShared}为
通过内部调用其中一个或多个检查功能来禁止驳运
方法,从而提供了一个<em>公平</em>的FIFO采集顺序。
特别是,大多数公平同步器可以定义{@code tryAcquire}。
返回{@code false},如果{@link #hasQueuedPredecessors}(一个方法)
专为公平同步器设计的)返回到
{@code true}。 其他的变化是可能的。
<p>一般来说,吞吐量和可扩展性是最高的。
默认驳船(又称<em>greedy</em>。
<em>放弃</em>,和<em>回避</em>)策略。
虽然这不能保证公平或无饥饿感,但早期的
队列中的线程允许在后面的队列中的线程之前重新约定
每一次和解都有不偏不倚的成功机会
针对传入的线程。 此外,虽然获取方法不
"旋转"在通常的意义上,它们可以执行多个
调用{@code tryAcquire}的调用与其他
阻止之前的计算。 这就给了大部分的好处
独家同步时的旋转,只需短暂地举行一次,而不需
大部分的责任时,它不是。如果你愿意的话,可以
通过在前面调用获取方法时,用
"fast-path "检查,可能是预先检查{@link #hasContended}。
和/或{@link #hasQueuedThreads},只有在同步器的条件下,才会这样做。
很可能不会被争论。
<p>这个类为我们提供了一个有效的、可扩展的基础。
同步化的部分原因是,将其使用范围专门用于
可以依赖{@code int}状态的同步器,获取、和
释放参数,以及内部FIFO等待队列。当这样做的时候
还不够,你可以从更低的层面上用
{@link java.utilite.concurrent.atomic atomic}类,你自己的自定义的
{@link java.utilite.Queueue}类,和{@link LockSupport}阻塞
支持。
*
* <h3>Usage Examples</h3>
*
* <p>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:
*
* <pre> {@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));
* }
* }}</pre>
*
* <p>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.
*
* <pre> {@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);
* }
* }}</pre>
*
* @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.
*
* <p>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.
*
* <p>To enqueue into a CLH lock, you atomically splice it in as new
* tail. To dequeue, you just set the head field.
* <pre>
* +------+ prev +-----+ +-----+
* head | | <---- | | <---- | | tail
* +------+ +-----+ +-----+
从上图来看,CLH锁,似乎只有一个prev,那么Node的next变量呢?
* </pre>
*
* <p>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.
*
* <p>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/
*
* <p>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.)
*
* <p>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.
*
* <p>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.
*
* <p>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.
*
* <p>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.
/**
等待队列节点类。
<p>等待队列是 "CLH "的变体(Craig,Landin,和
Hagersten)锁队列。CLH锁通常用于
旋转锁。 我们反而用它们来阻断同步器,但
牵一发而动全身
节点的前辈中的线程信息。 每个节点中的 "状态 "字段记录了一个线程是否有一个
应该阻止。 当一个节点的前任
释放。 队列的每一个节点,否则就作为一个
专项通知式监控器,手持单机等待
线程。状态字段不控制线程是否为
授予锁等,但。 一个线程可以尝试获取,如果它是
拔得头筹 但第一并不能保证成功。
它只是赋予了竞争的权利。 所以,目前发布的
竞争者线程可能需要重新等待。
<p>要入队到一个CLH锁,你可以将其原子化拼接为new
尾部。要取消队列,只需设置头部字段即可。
<P>插入到CLH队列中只需要一个原子字段
尾巴 "上的操作,所以有一个简单的原子点的
从未排队到排队的分界。同样,取消排队
只涉及更新 "头"。然而,这需要一点
节点的工作更多的是确定谁是接班人。
部分原因是为了处理可能因超时而取消的问题。
和中断。
<p>"prev "链接(原CLH锁中没有使用),主要是指的是
处理取消所需。如果一个节点被取消,其
继承者(通常)重新链接到一个未取消的
前任。对于类似的机理的解释
的自旋锁,见Scott和Schererer的论文,在
http://www.cs.rochester.edu/u/scott/synchronization/
<P>我们还使用 "下一个 "链接来实现阻塞机制。
每个节点的线程id都保存在自己的节点中,所以一个
前任的节点通过遍历
下一个链接来确定是哪条线程。 判断是哪个线程
继任者必须避开与新排队节点的比赛,以设置
的 "下一个 "领域的前辈们。 这个问题就解决了
必要时,从原子上倒查
当一个节点的继任者似乎是空的时候,更新 "尾巴"。
(或者换个说法,下一个链接是一种优化的方法。
这样我们通常就不需要逆向扫描了)。
<P>取消引入了一些保守的基本的
算法。 由于我们必须对取消其他
节点,我们可能会错过注意到一个被取消的节点是否为
在我们的前面或后面。这个问题的处理方法是,始终不停车
取消后的继承人,使他们能够稳定在
新的前任,除非我们能确定一个未取消的
前辈将承担起这个责任。
<p>CLH队列需要一个假头节点来启动。但是
我们不在施工中创造它们,因为这样做是浪费了
努力,如果从来没有争论的话。相反,节点
构建,并在第一时间设置头部和尾部指针。
争论。
<p>等待条件的线程使用相同的节点,但
使用附加链接。条件只需要链接节点
在简单(非并发)链接队列中,因为它们是
只有在独占时才会被访问。 在等待时,一个节点被
插入到条件队列中。 一旦收到信号,该节点将
转移到主队列中。 状态的特殊值(即SHARED)
字段用于标记一个节点在哪个队列中。
<p>感谢 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值是否允许,资源足够则获取
*
* <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的线程执行,如果获取失败了,那么把线程入队(如果还没入队),直到其他线程调用release方法发出了信号释放;
*
* <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.
*
* <p>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.
共享模式下获取资源,这个方法应该首先查询看资源量是否足够..
*
* <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.
*
* <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.
如果获取失败返回负数,如果获取成功但没有其他资源(即其他的线程无法再成功获取)返回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.
*
* <p>This method is always invoked by the thread performing release.
尝试设置state,以反映共享模式下的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
如果这个共享模式的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
*
* <p>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呢? 是的,但是这个已经没有更好办法处理了, 只能说无法保证。
*
* <p>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方法被阻塞过
*
* <p>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
*
* <p>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
简单说,其实获取head的next对应的线程
*/
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)) 全是赋值和做非空判断; || 符号两边都是完全一样的代码,为什么这样做?为了保证读操作的一致性?
这里其实就是判断 head和它的next 是不是非空,然后返回next节点的线程; 其实是第二个..
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字段可能还未被置,或者被unset,或者还未设置(还未初始化?),所以我们需要检查 tail 是不是就是第一个node;如果不是,我们反向遍历(安全起见)然后找到First Queued Thread
*/
Node t = tail;
Thread firstThread = null;
while (t != null && t != head) {
Thread tt = t.thread;
if (tt != null)
firstThread = tt; 返回head后面的那个线程
t = t.prev;
}
return firstThread;
}
/**
* Returns true if the given thread is currently queued.
判断给定的线程是否已经入队
*
* <p>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,
*
* <p>An invocation of this method is equivalent to (but may be
* more efficient than): 效果等同于下面方法:
* <pre> {@code
* getFirstQueuedThread() != Thread.currentThread() &&
* hasQueuedThreads()}</pre>
*
* <p>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
*
* <p>This method is designed to be used by a fair synchronizer to
* avoid <a href="AbstractQueuedSynchronizer#barging">barging</a>. 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 方法示例如下:
*
* <pre> {@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
* }
* }}</pre>
*
* @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
换句话说 就是判断 FIFO队列中是否还有等待的线程
* @since 1.7
*/
顾名思义,这个方法是判断是否有已经入队的前任;即判断是不是第二个node。
public final boolean hasQueuedPredecessors() { 这个方法其实仅仅在ReentrantLock.FairSync#tryAcquire被调用过一次!!
// 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<Thread> getQueuedThreads() {
ArrayList<Thread> list = new ArrayList<Thread>();
for (Node p = tail; p != null; p = p.prev) {
Thread t = p.thread;
if (t != null)
list.add(t);————同上方法,可以理解为不包括头结点
}
return list;
}
/**
* Returns a collection containing threads that may be waiting to
* acquire in exclusive mode. This has the same properties
* as {@link #getQueuedThreads} except that it only returns
* those threads waiting due to an exclusive acquire.
返回同步队列中的独占模式下等待的线程的集合,同上方法getQueuedThreads,但是仅仅返回其中由于执行独占模式acquire导致阻塞的线程;
———— 这就说明了, 一个CLH同步队列,其实既可以同时存在共享模式的node,也可以存在独占模式的node!!~~~
———— 队列本身没有做限制;———— 比如可读写锁,可能就同时存在这两个模式的node !!
exclusive 独占,也可以理解为排他;
*
* @return the collection of threads
*/
public final Collection<Thread> getExclusiveQueuedThreads() {
ArrayList<Thread> list = new ArrayList<Thread>();
for (Node p = tail; p != null; p = p.prev) {
if (!p.isShared()) { 判断是否独占。
Thread t = p.thread;
if (t != null) ————同上方法,可以理解为不包括头结点
list.add(t);
}
}
return list;
}
/**
* Returns a collection containing threads that may be waiting to
* acquire in shared mode. This has the same properties
* as {@link #getQueuedThreads} except that it only returns
* those threads waiting due to a shared acquire.
返回同步队列中的独占模式下等待的线程的集合,同上方法getQueuedThreads,但是仅仅返回其中由于执行共享模式acquire导致阻塞的线程;
*
* @return the collection of threads
*/
public final Collection<Thread> getSharedQueuedThreads() {
ArrayList<Thread> list = new ArrayList<Thread>();
for (Node p = tail; p != null; p = p.prev) {
if (p.isShared()) { 判断是否共享。
Thread t = p.thread;
if (t != null)
list.add(t);
}
}
return list;
}
/**
* Returns a string identifying this synchronizer, as well as its state.
* The state, in brackets, includes the String {@code "State ="}
* followed by the current value of {@link #getState}, and either
* {@code "nonempty"} or {@code "empty"} depending on whether the
* queue is empty.
*
* @return a string identifying this synchronizer, as well as its state
*/
public String toString() {
int s = getState();
String q = hasQueuedThreads() ? "non" : "";
return super.toString() +
"[State = " + s + ", " + q + "empty queue]";
}
// Internal support methods for Conditions
/**
* Returns true if a node, always one that was initially placed on
* a condition queue, is now waiting to reacquire on sync queue.
判断node是否位于同步队列,———— 通常一个node刚被创建出来后,他就是位于条件队列。
因为isOnSyncQueue仅用于ConditionObject内部,此种的Node开始是位于条件队列的,这里就是判断它释放被转移到了同步队列,
* @param node the node
* @return true if is reacquiring 如果node正在尝试重新获取资源,返回true
*/
final boolean isOnSyncQueue(Node node) {
if (node.waitStatus == Node.CONDITION || node.prev == null) 状态为CONDITION 或者prev指针为null,
return false;
if (node.next != null) // If has successor, it must be on queue 如果next不为空,那么必然位于同步队列
return true;
/*
* node.prev can be non-null, but not yet on queue because
* the CAS to place it on queue can fail. So we have to
* traverse from tail to make sure it actually made it. It 连续3个it,英语不好真的容易懵逼..
* will always be near the tail in calls to this method, and
* unless the CAS failed (which is unlikely), it will be
* there, so we hardly ever traverse much.
因为CAS入队操作可能失败, node.prev 可能已经不是空但仍然未入队( 这个时候,可能next还未设置)。所以我们需要从tail反向遍历以确保it made it (?前面的it是? 后面的it? )应该是指正在入队这个事情, 它总是在tail附近,为了调用这个方法;除非cas入队失败; 它将会在那里,所以我们不会遍历太多。
这里的it 真是难以翻译。。 这大师的思维难以理解啊。英语不好真的容易懵逼。
这里的it,它,应该是指node
*/
return findNodeFromTail(node); 从尾到头的方式进行查找node,找到了则说明它位于同步队列,返回true
为什么需要从尾到头的方式? 按照上诉描述,是因为 next指针靠不住,prev指针更加靠谱..xxx
}
/**
* Returns true if node is on sync queue by searching backwards from tail.
* Called only when needed by isOnSyncQueue.
* @return true if present
*/
private boolean findNodeFromTail(Node node) {
Node t = tail;
for (;;) {
if (t == node)
return true; 找到就true
if (t == null)
return false; 遇到了null,通常就是说明已经到了head,还没找到则返回false
t = t.prev;
}
}
/**
* Transfers a node from a condition queue onto sync queue.
* Returns true if successful.
将一个node从条件队列转移到同步队列,转移成功返回true
* @param node the node
* @return true if successfully transferred (else the node was
* cancelled before signal)
*/
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) cas也不是说必然要配合自旋一起操作。。
return false; 如果已经被取消了,那么返回false
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
衔接到同步队列(也就是从条件队列中出队,入队到同步队列中),然后试着设置node的前任的waitStatus为SIGNAL; 如果设置或者被取消,那么直接唤醒node对应的线程,让它重新去同步(这种情况下,其状态值会被短暂但无害的设置错误)
———— 为什么这种情况就是 错误的?因为cas失败了,此时waitStatus不是逻辑上合理的值
———— 为什么这种情况 无害呢? 因为我们把node唤醒,它会尝试去自旋的方式获取资源( 唤醒之前应该是在执行 acquireQueued 方法之中,唤醒之后继续acquireQueued的自旋.. ),因为它还有前任,只要前任还没释放,那么这个node 是无法获取到资源的,那么就会执行shouldParkAfterFailedAcquire,那么又会把前任的waitStatus的值设置为SIGNAL, 这个时候,虽然前任的状态有短暂的错误,但是harmless !
*/
Node p = enq(node); // 入队
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) //
LockSupport.unpark(node.thread); cas失败了,此时前任的waitStatus 还是保持之前的值
return true;
}
/**
* Transfers node, if necessary, to sync queue after a cancelled wait. //cancelled wait 是什么?
* Returns true if thread was cancelled before being signalled.
(应该指在条件队列上的等待)等待取消后,如果有必要,那么将一个node从条件队列转移到同步队列;
如果线程 被唤醒之前 被取消,返回true ———— 看代码可知,其实有一个cas操作
———— 等待为什么会失败? 什么叫做有必要? xxx 什么是CancelledWait? 感觉是这样: 当前node,本来是在条件队列上等待,但是被取消了,那么就应该回归到同步队列
*
* @param node the node
* @return true if cancelled before the node was signalled 其实这个描述非常准确!!
返回值也是很难理解。虽然难理解,但是这个返回值很关键,不得不去理解
正常来说,node的状态应该是CONDITION,然后就设置它的状态呗,然后就入队到同步队列,然后就算正常完成,返回true;
但是,存在其他的 难以理喻的情况,比如transferForSignal修改了node的状态,那么cas 失败了,
*/
这个方法是仅仅是在中断或者超时的时候被调用。表示由于中断或者超时,node不需要继续在条件队列上阻塞了,把它转移到同步队列吧!~
final boolean transferAfterCancelledWait(Node node) { 这个方法也很生涩..
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) { cas设置状态为0 —— 为0表示刚入队的状态
enq(node); 设置成功了则入队(进入同步队列) 这里enq肯定会成功,因为enq包含在自旋之内
return true; 为什么cas成功就返回true,因为enq肯定会成功,
}
/*
* If we lost out to a signal(), then we can't proceed
* until it finishes its enq(). Cancelling during an
* incomplete transfer is both rare and transient, so just
* spin.
如果在signal()方法中失利,那么我们无法继续处理,只能等待它入队成功; 在一次不完全的转移操作中的取消操作 是非常罕见而且短暂的,所以我们仅仅自旋一下即可。
cas失败意味着node的状态已经被其他线程改变,这个改变的可能情况是:其他的线程先执行到上面的cas..
*/
while (!isOnSyncQueue(node)) 如果node不位于同步队列,
Thread.yield(); 那么让出cpu执行分片,即让出cpu执行权,进入ready状态等待下一个cpu执行分片
———— 为什么会这样,这个仅仅是因为转移还未完成, 必须需要等待它,确保node回归到同步队列!
———— 这里仅仅是等待,那么实际的入队操作在哪里进行?
———— 更新 2020年4月6日 : 其实需要等待enq的完成,就是说虽然cas失败了,但是enq是需要确保执行完毕的,不管是当前线程来执行enq,还是其他线程执行enq也好。 总之需要 node进入同步队列之后,才结束这个方法!
return false; 到这里,说明转移失败了。 为什么是失败,因为它没有进入到同步队列。为什么没有?
感觉这里有些矛盾xxx:如果cas成功了,也是确保入队;如果失败也也要自旋确保处于同步队列
更新 2020年4月6日 :
到return这里,说明当前线程cas竞争失败,其实就是其他线程抢先一步改变了node的状态,;await不存在竞争,说明是signal方法捣的鬼,transfer之前就已经被其他线程唤醒! 既然没有达到目的(重新入队),既然已经被唤醒,那么说明当前线程已经迟了一步,那么就返回false吧
尽管非常罕见,但是仍然有可能,比如当前线程正在条件队列中等待(响应超时和中断的方式),但是被打断,那么就不应该继续等待,准备让它重新进入同步队列。 入队的时候再次检查状态,发现呢,node刚刚前一个瞬间好被唤醒,那么我还是等待它完成入队;然后返回false,表示没有完成指定的逻辑语义(预设的语义就是在顺利完成入队)。 超时也是一样的逻辑。
}
注: 对于可能被多线程同时访问的volatile变量,最好通过cas来进行改变它(即使没有自旋),即使此时,当前方法没有同步,但不能保证所有的地方都不存在同步。 如果能保证,那么确实也可以不用cas
/**
* Invokes release with current state value; returns saved state.
* Cancels node and throws exception on failure.
完全的释放node对应线程占用的资源,也就是之前获取的资源; 如果不能完全释放,那么取消它并抛异常
* @param node the condition node for this wait 参数node是条件队列上等待的节点;
* @return previous sync state 返回之前占用的同步资源量
*/
final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState(); 获取之前获取的资源量
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED; 要确保node得到取消;为什么要取消?取消意味着它将被忽视,而且很快被删除;—— 大概因为特殊的业务逻辑需要
}
}
// Instrumentation methods for conditions
/**
* Queries whether the given ConditionObject
* uses this synchronizer as its lock.
查询给定的条件对象是不是使用当前AQS作为它的锁
*
* @param condition the condition
* @return {@code true} if owned
* @throws NullPointerException if the condition is null
*/
public final boolean owns(ConditionObject condition) {
return condition.isOwnedBy(this);
}
/**
* Queries whether any threads are waiting on the given condition
* associated with this synchronizer. Note that because timeouts
* and interrupts may occur at any time, a {@code true} return
* does not guarantee that a future {@code signal} will awaken
* any threads. This method is designed primarily for use in
* monitoring of the system state.
查询是否有线程正在给定的当前AQS的条件对象上等待;
由于超时、中断,返回true并不保证接下来的唤醒操作能够真正的唤醒某个线程(因为它可能已经被取消,可能nulled out,无须或者无法再去唤醒它)
这个方法主要设计用于模拟系统状态xxx
*
* @param condition the condition
* @return {@code true} if there are any waiting threads
* @throws IllegalMonitorStateException if exclusive synchronization
* is not held
* @throws IllegalArgumentException if the given condition is
* not associated with this synchronizer
* @throws NullPointerException if the condition is null
*/
public final boolean hasWaiters(ConditionObject condition) {
if (!owns(condition))
throw new IllegalArgumentException("Not owner");
return condition.hasWaiters();
}
/**
* Returns an estimate of the number of threads waiting on the
* given condition associated with this synchronizer. Note that
* because timeouts and interrupts may occur at any time, the
* estimate serves only as an upper bound on the actual number of
* waiters. This method is designed for use in monitoring of the
* system state, not for synchronization control.
获取条件等待队列的长度,仅能获取到一个大概的数值(实际值的上限),因为..
这个方法设计用于模拟系统状态xxx 而不是同步器的控制。
*
* @param condition the condition
* @return the estimated number of waiting threads
* @throws IllegalMonitorStateException if exclusive synchronization
* is not held
* @throws IllegalArgumentException if the given condition is
* not associated with this synchronizer
* @throws NullPointerException if the condition is null
*/
public final int getWaitQueueLength(ConditionObject condition) {
if (!owns(condition))
throw new IllegalArgumentException("Not owner");
return condition.getWaitQueueLength();
}
/**
* Returns a collection containing those threads that may be
* waiting on the given condition associated with this
* synchronizer. 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.
获取条件等待队列的集合,仅能获取到一个大概的数值(尽最大努力 best-effort),无特殊排序xxx..
*
* @param condition the condition
* @return the collection of threads
* @throws IllegalMonitorStateException if exclusive synchronization
* is not held
* @throws IllegalArgumentException if the given condition is
* not associated with this synchronizer
* @throws NullPointerException if the condition is null
*/
public final Collection<Thread> getWaitingThreads(ConditionObject condition) {
if (!owns(condition))
throw new IllegalArgumentException("Not owner");
return condition.getWaitingThreads();
}
/**
* Condition implementation for a {@link
* AbstractQueuedSynchronizer} serving as the basis of a {@link
* Lock} implementation.
ConditionObject是Condition的具体实现; 作为Lock的基础实现
*
* <p>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
这个类的出口版本,通常也需要描述条件语义的文档 伴随
*
* <p>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.
* <ol>
* <li> Save lock state returned by {@link #getState}.
* <li> Invoke {@link #release} with saved state as argument,
* throwing IllegalMonitorStateException if it fails.
* <li> Block until signalled.
* <li> Reacquire by invoking specialized version of
* {@link #acquire} with saved state as argument.
* </ol>
*/
不响应中断。 await() 和这个方法那个好一些? 什么情况用这个方法? 为什么默认的 await() 响应中断,我感觉await()被中断了也不要紧,它还是需要入队阻塞方式去获取资源
public final void awaitUninterruptibly() {
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
boolean interrupted = false;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if (Thread.interrupted())
interrupted = true;
}
if (acquireQueued(node, savedState) || interrupted)
selfInterrupt();
}
/*
* For interruptible waits, we need to track whether to throw
* InterruptedException, if interrupted while blocked on
* condition, versus reinterrupt current thread, if
* interrupted while blocked waiting to re-acquire.
*/
/** Mode meaning to reinterrupt on exit from wait */
意味着等待结束之后 需要重新进行中断
private static final int REINTERRUPT = 1;
/** Mode meaning to throw InterruptedException on exit from wait */
意味着等待结束之后 需要抛异常
private static final int THROW_IE = -1;
/**
* Checks for interrupt, returning THROW_IE if interrupted
* before signalled, REINTERRUPT if after signalled, or
* 0 if not interrupted.
检查interrupt状态 如果被唤醒之前就中断,那么抛异常;如果是之后,那么重新中断;
如果从未被中断,那么返回0, 为什么这样的处理?
为什么“被唤醒之前就中断,那么抛异常” ? 这个是因为..
*/
private int checkInterruptWhileWaiting(Node node) { 这个方法也很生涩
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) : 标记一下是应该抛异常还是重新中断
0;
}
/**
* Throws InterruptedException, reinterrupts current thread, or
* does nothing, depending on mode.
等待结束之后,报告中断情况
*/
private void reportInterruptAfterWait(int interruptMode)
throws InterruptedException {
if (interruptMode == THROW_IE)
throw new InterruptedException(); THROW_IE就抛异常
else if (interruptMode == REINTERRUPT)
selfInterrupt(); REINTERRUPT就重新设置中断..
}
/**
* Implements interruptible condition wait.
* <ol>
* <li> If current thread is interrupted, throw InterruptedException.
* <li> Save lock state returned by {@link #getState}.
* <li> Invoke {@link #release} with saved state as argument,
* throwing IllegalMonitorStateException if it fails.
* <li> Block until signalled or interrupted.
* <li> Reacquire by invoking specialized version of
* {@link #acquire} with saved state as argument.
* <li> If interrupted while blocked in step 4, throw InterruptedException.
* </ol>
可中断条件等待的实现,具体步骤:
1 如果已经被中断,抛异常
2 先把资源量的值暂且保留
3 调用release,以 释放前面保存的 资源量
4 阻塞,知道被信号唤醒 或者中断
5 使用特殊版本的方法 重新获取(其实就是acquireQueued)资源
6 如果步骤4中断过,那么,抛异常
*/
首先需要理解:执行到await(), 当前node 其实就是head,当前线程就是正在已经获取到资源,正在同步区域执行的线程!
简单来说,这个方法的作用是:
1 把当前线程添加到 当前条件对象内部的 条件队列。
2 释放所有线程占用的所有的资源 —— 释放的时候可能抛异常或者失败取消
3 唤醒后继,(其实就是让后继变成新的head,处于一种执行的状态)
4 阻塞当前线程,直到被唤醒、同时还需要处于等待队列 —— 如果位于同步队列则跳出循环;
5 被唤醒后,进入同步队列,重新获取资源
5 获取资源成功后继续await之后的代码
6 获取资源失败则进行异常处理。
对中断的处理:
如果已经释放资源之前就被中断,抛异常
如果在同步队列的时候被中断,则
问题: await() 要不要放入while循环条件中,就像Object的wait()方法 ? 我感觉也是需要的,保险起见,因为多线程的情形条件时刻在变化,如果线程醒来之后,发现条件立即又不满足了, 也是有可能的; 比如释放了一个资源,唤醒了两个线程(正常的逻辑是唤醒一个线程),然后一个线程抢到了资源,另外一个线程就会阻塞(没法获取资源然后从acquireQueued退出) 这种情况不需要。
问题:一个node是否可能即位于同步队列,又位于条件队列? 可能的,即***处
什么情况需要对一个条件队列中的等待状态的节点 执行取消, 还是 因为中断或者超时;
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException(); 如果已经被中断,抛异常
Node node = addConditionWaiter(); 把当前线程添加到条件队列;
int savedState = fullyRelease(node); 完全的释放AQS被占用的资源;而不是fullyRelease的参数node占用的资源。
为什么需要完全的释放资源?
如果只释放一部分的资源,是否也可以呢?我感觉也是可以让其他线程进入的,但是考虑只能是独占模式,所以,如果只是释放部分资源,意味着当前线程还在占用部分资源,由于是独占模式,不能共享资源,其他线程还是无法获得资源得以执行; 故这里一定是释放所有;
fullyRelease内部调用release,release会减少占用的资源、并唤醒head的后继的线程;代码里面也是tryRelease了 All资源 成功了,才会唤醒后继。 执行到release, 后继线程已经醒过来了!! 但是当前线程需要一些其他操作把自己陷入阻塞;如下while内部的逻辑
fullyRelease 意味着当前线程出队(虽然其实并没执行完毕同步区域代码,但是需要进行条件上的等待,于是也只得让出老大的地位,自己隐居幕后~ 原同步队列的旧head会被GC~)
int interruptMode = 0;
while (!isOnSyncQueue(node)) { 循环判断是否处于同步队列,已经处于了,那么就退出循环;
node 是刚刚addConditionWaiter新建处理的节点,创建出来的时候就是不位于同步队列,那么什么情况下它会位于同步队列? 答案是被取消,或者被signal 唤醒; 唤醒有其他线程来进行, 取消是谁来调用的?其实是中断或者未知异常..
如果node处于同步队列,说明它被唤醒或者中断了~!
LockSupport.park(this); 其他线程得以执行,但是自己需要进行 阻塞
// 执行到下一行,肯定是被唤醒了,那么怎么被唤醒的呢? 可能是被中断,可能是被signal
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) 检查一下 如果等待过程中曾经被中断,那么也要跳出循环!~
break; 被中断了也要 执行checkInterruptWhileWaiting,确保enq, 这样下面的 acquireQueued才会可能成功
}
// 位置*** 执行到这里,如果node 中断或超时,那么它即位于条件队列,又位于 同步队列,
if (acquireQueued(node, savedState) && interruptMode != THROW_IE) 如果获取成功而且未被标记为抛出异常,那么,那么标记一下,需要重新的中断
interruptMode此时可能==0,但是因为acquireQueued 返回true 意味着被中断过,所以这里也需要重新的 去中断!!
interruptMode = REINTERRUPT;
acquireQueued 其实是一个可能引起阻塞的方法,acquireQueued返回了,意味着获取资源成功或者异常!
如果线程在 调用acquireQueued 的时候阻塞了,说明它前面还有线程;acquireQueued成功了node变成head,还是位于同步队列。虽然node现在还是位于条件队列,但是它的状态不再是CONDITION;只要入同步队列,状态就不会是CONDITION
node什么时候会从条件队列中消失? 好像只有 doSignal 和 unlinkCancelledWaiters
node什么时候会从同步队列中消失? 被取消或者获取到锁执行完毕出队。
if (node.nextWaiter != null) // clean up if cancelled 如果被取消,则清理一下整个条件队列
nextWaiter 不为空有两种情况,1 处于等待队列、 2 共享模式 ; 这里不考虑共享模式,所以肯定是1
nextWaiter 为空则意味着位于 同步队列
前面的while 确保了node位于同步队列才会执行到这里,除非被中断; 这里正是考虑被中断的情况, 是一种异常情况!
为什么不通过 interruptMode 进行判断? 因为.. interruptMode可能为0 所以无法判断?
unlinkCancelledWaiters(); 到这里,node仍然处于条件队列,但是被中断,所以需要 执行一次 移除: 把node 以及其他的所有状态非CONDITION的 node 从条件队列中删除!(其实是绕过了它,然后让它被GC)
执行到这里,线程已经处于同步队列的头、是队列中正在执行的线程;
if (interruptMode != 0) 如果不是0,就不是默认值,说明 要么就是需要中断,要么就是需要抛异常
reportInterruptAfterWait(interruptMode); 执行到这里,如果之前有异常,要补上,重新抛异常! 相当于前面有异常,也是被抑制了!~; 如果有中断,也补上( 这个时候不会抛异常,而仅仅是 在线程上 标记一下)!
执行到这里,说明没有异常,await方法正式结束,回归到lock方法的内部,执行下一行的业务代码!!
}
/**
* Implements timed condition wait.
* <ol>
* <li> If current thread is interrupted, throw InterruptedException.
* <li> Save lock state returned by {@link #getState}.
* <li> Invoke {@link #release} with saved state as argument,
* throwing IllegalMonitorStateException if it fails.
* <li> Block until signalled, interrupted, or timed out. 阻塞,除非被唤醒、打断、超时
* <li> Reacquire by invoking specialized version of 重新读取资源; specialized version就是指acquireQueued,独占模式获取
* {@link #acquire} with saved state as argument.
* <li> If interrupted while blocked in step 4, throw InterruptedException. 如果被打断,也要抛异常!
* </ol>
限时的条件等待;
如果被打断,那么抛异常
...
Reacquire
..
*/
public final long awaitNanos(long nanosTimeout) 响应超时和中断。
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node); 也是需要完全释放
final long deadline = System.nanoTime() + nanosTimeout;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (nanosTimeout <= 0L) {
transferAfterCancelledWait(node); 如果超时时间非正数,那么就当做它被取消!! 理解这点很重要! 就是说,如果我想取消条件等待,那么传递一个超时时间为非正数 即可..
可是,谁会传递超时时间非正数? 是不是傻?
break; 及时响应,立即退出循环
}
if (nanosTimeout >= spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;// interruptMode != 0意味着出现了中断或者异常,那么立即响应,退出循环!
nanosTimeout = deadline - System.nanoTime();
}
执行下面的3个if,也是需要时间的,虽然可能是非常非常少的时间,但是也是肯定多多少少需要一点时间的。但是nanosTimeout 不考虑这些时间
// acquireQueued的作用是让node进入同步队列重新去获取资源,获取不到则会陷入阻塞
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT; interruptMode是之前while返回的,此处进行判断,然后重新中断处理!
if (node.nextWaiter != null) 如果nextWaiter不为空,意味着node还在条件队列,
unlinkCancelledWaiters(); 到这里,node仍然处于条件队列,但是被中断,所以需要 执行一次 移除
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return deadline - System.nanoTime();
返回值的解释位于接口:
an estimate of the nanosTimeout value minus the time spent waiting upon return from this method. A positive value may be used as the argument to a subsequent call to this method to finish waiting out the desired time. A value less than or equal to zero indicates that no time remains.
返回一个大概的值: 最大允许的超时时间 减去 线程这个方法到return语句的时间。 正值意味着还有时间剩余,亦即没有超时; 0或者负值意味着没有时间剩余了; 上面做了超时限制,为什么还会出现负值? 其实是因为没法做到严格的精确,比如上面的3个if 也是需要时间的; 如果超时, 加上上面3个if的时间, 那么 线程执行到return 的时候 确实会大于参数的nanosTimeout
这个返回值 可以用于 subsequent call,以完成 等待指定的时间。(反正返回值肯定也是有点用的)
}
/**
* Implements absolute timed condition wait.
* <ol>
* <li> If current thread is interrupted, throw InterruptedException.
* <li> Save lock state returned by {@link #getState}.
* <li> Invoke {@link #release} with saved state as argument,
* throwing IllegalMonitorStateException if it fails.
* <li> Block until signalled, interrupted, or timed out.
* <li> Reacquire by invoking specialized version of
* {@link #acquire} with saved state as argument.
* <li> If interrupted while blocked in step 4, throw InterruptedException.
* <li> If timed out while blocked in step 4, return false, else true.
* </ol>
*/
public final boolean awaitUntil(Date deadline) 非常类似awaitNanos
throws InterruptedException {
long abstime = deadline.getTime();
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node); 也是需要完全释放
boolean timedout = false;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (System.currentTimeMillis() > abstime) {
timedout = transferAfterCancelledWait(node);
break;
}
LockSupport.parkUntil(this, abstime);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return !timedout;
}
/**
* Implements timed condition wait.
* <ol>
* <li> If current thread is interrupted, throw InterruptedException.
* <li> Save lock state returned by {@link #getState}.
* <li> Invoke {@link #release} with saved state as argument,
* throwing IllegalMonitorStateException if it fails.
* <li> Block until signalled, interrupted, or timed out.
* <li> Reacquire by invoking specialized version of
* {@link #acquire} with saved state as argument.
* <li> If interrupted while blocked in step 4, throw InterruptedException.
* <li> If timed out while blocked in step 4, return false, else true.
* </ol>
*/
public final boolean await(long time, TimeUnit unit) 非常类似awaitNanos,不过单位不是纳秒
throws InterruptedException {
long nanosTimeout = unit.toNanos(time);
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node); 也是需要完全释放
final long deadline = System.nanoTime() + nanosTimeout; 同样需要计算deadline
boolean timedout = false;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (nanosTimeout <= 0L) {
timedout = transferAfterCancelledWait(node);
break;
}
if (nanosTimeout >= spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
nanosTimeout = deadline - System.nanoTime();
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return !timedout;
}
// support for instrumentation
/**
* Returns true if this condition was created by the given
* synchronization object.
判断是否由给定的同步器对象 创建的
*
* @return {@code true} if owned
isOwnedBy 其实就是检查 owner
*/
final boolean isOwnedBy(AbstractQueuedSynchronizer sync) {
return sync == AbstractQueuedSynchronizer.this;
}
/**
* Queries whether any threads are waiting on this condition.
* Implements {@link AbstractQueuedSynchronizer#hasWaiters(ConditionObject)}.
查询是否有任何线程在当前条件对象上等待, (也就是正在等待某个条件的满足)
*
* @return {@code true} if there are any waiting threads
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
* returns {@code false}
*/
protected final boolean hasWaiters() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
if (w.waitStatus == Node.CONDITION)
return true;
}
return false;
}
/**
* Returns an estimate of the number of threads waiting on
* this condition.
* Implements {@link AbstractQueuedSynchronizer#getWaitQueueLength(ConditionObject)}.
返回一个大概的条件队列中等待的node的总数值
*
* @return the estimated number of waiting threads
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
* returns {@code false}
*/
protected final int getWaitQueueLength() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
int n = 0;
for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
if (w.waitStatus == Node.CONDITION) 仍然处于等待状态,才算一个;
++n;
}
return n;
}
/**
* Returns a collection containing those threads that may be 这里用到了may be 也表示只能大概的
* waiting on this Condition.
返回一个大概的条件队列中等待的node的集合
* Implements {@link AbstractQueuedSynchronizer#getWaitingThreads(ConditionObject)}.
*
* @return the collection of threads
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
* returns {@code false}
*/
protected final Collection<Thread> getWaitingThreads() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
ArrayList<Thread> list = new ArrayList<Thread>();
for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
if (w.waitStatus == Node.CONDITION) { 仍然处于等待状态,才算一个;
Thread t = w.thread;
if (t != null) 保守的进行判断,防止各种意外
list.add(t);
}
}
return list;
}
}
/**
* Setup to support compareAndSet. We need to natively implement
* this here: For the sake of permitting future enhancements, we
* cannot explicitly subclass AtomicInteger, which would be
* efficient and useful otherwise. So, as the lesser of evils, we
* natively implement using hotspot intrinsics API. And while we
* are at it, we do the same for other CASable fields (which could
* otherwise be done with atomic field updaters).
*/
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;
static {
try {
stateOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
headOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
tailOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
waitStatusOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("waitStatus"));
nextOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("next"));
} catch (Exception ex) { throw new Error(ex); }
}
/**
* CAS head field. Used only by enq.
*/
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
/**
* CAS tail field. Used only by enq.
*/
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
/**
* CAS waitStatus field of a node.
*/
private static final boolean compareAndSetWaitStatus(Node node,
int expect,
int update) {
return unsafe.compareAndSwapInt(node, waitStatusOffset,
expect, update);
}
/**
* CAS next field of a node.
*/
private static final boolean compareAndSetNext(Node node,
Node expect,
Node update) {
return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
}
}