注意してください、迷わないでください!この記事があなたに役立つなら、好きで、サポートすることを忘れないでください!
1.条件定義
Conditionは、制御スレッドがロックを解放し、ロックを取得した他のスレッドがウェイクアップするシグナル信号を送信するのを待つために、JUCで提供されるツールクラス
です。主な機能:
- 内部の条件は、スレッドノードNodeをロードする条件キューによって主に実装されます
- Conditionメソッドの呼び出し(待機、シグナルなど)は、スレッドが排他ロックを取得していることを前提とする必要があります。
- Conditionの操作方法の前提は排他ロックを取得することであるため、Condition Queueの内部は、同時実行の安全性をサポートしない一方向のキューです(これはAQSの同期キューに関連しています)。
最初に一般的なデモを見てください
import org.apache.log4j.Logger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 此demo用于测试 condition
* Created by xujiankang on 2017/2/8.
*/
public class ConditionTest {
private static final Logger logger = Logger.getLogger(ConditionTest.class);
static final Lock lock = new ReentrantLock();
static final Condition condition = lock.newCondition();
public static void main(String[] args) throws Exception{
final Thread thread1 = new Thread("Thread 1 "){
@Override
public void run() {
lock.lock(); // 线程 1获取 lock
logger.info(Thread.currentThread().getName() + " 正在运行 .....");
try {
Thread.sleep(2 * 1000);
logger.info(Thread.currentThread().getName() + " 停止运行, 等待一个 signal ");
condition.await(); // 调用 condition.await 进行释放锁, 将当前节点封装成一个 Node 放入 Condition Queue 里面, 等待唤醒
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info(Thread.currentThread().getName() + " 获取一个 signal, 继续执行 ");
lock.unlock(); // 释放锁
}
};
thread1.start(); // 线程 1 线运行
Thread.sleep(1 * 1000);
Thread thread2 = new Thread("Thread 2 "){
@Override
public void run() {
lock.lock(); // 线程 2获取lock
logger.info(Thread.currentThread().getName() + " 正在运行.....");
thread1.interrupt(); // 对线程1 进行中断 看看中断后会怎么样? 结果 线程 1还是获取lock, 并且最后还进行 lock.unlock()操作
try {
Thread.sleep(2 * 1000);
}catch (Exception e){
}
condition.signal(); // 发送唤醒信号 从 AQS 的 Condition Queue 里面转移 Node 到 Sync Queue
logger.info(Thread.currentThread().getName() + " 发送一个 signal ");
logger.info(Thread.currentThread().getName() + " 发送 signal 结束");
lock.unlock(); // 线程 2 释放锁
}
};
thread2.start();
}
}
実行ステップ全体
- スレッド1は実行を開始し、ロックを取得して、2秒間スリープします。
- スレッド1が1秒間スリープすると、スレッド2が実行を開始しますが、ロックはスレッド1によって取得されるため、待機します。
- スレッド1は2秒間スリープし、condition.await()を呼び出してロックを解放し、スレッド1をノードにカプセル化して条件の条件キューに入れ、ロックを取得した他のスレッドがシグナルを送信するか、シグナルを中断する(割り込みする)のを待ちます(同期キューに移動してロックを取得できます)
- スレッド2はロックを正常に取得し、スレッド1を中断します。スレッドが中断された後、ノードは条件キューから同期キューに転送されますが、ロックはスレッド2によって引き続き取得されるため、ノードは同期キューに留まり、ロックの取得を待機します
- スレッド2が2秒間スリープし、信号を使用して条件キュー内のノードをウェイクアップし始めました(この時点で、スレッド1を表すノードが同期キューに到着しました)
- スレッド2はロックを解放し、同期キューでロックを取得するために待機しているノードnodeをウェイクアップします
- スレッド1が起こされ、ロックを取得します
- スレッド1がロックを解放します
の結果
[2017-02-08 22:43:09,557] INFO Thread 1 (ConditionTest.java:26) - Thread 1 正在运行 .....
[2017-02-08 22:43:11,565] INFO Thread 1 (ConditionTest.java:30) - Thread 1 停止运行, 等待一个 signal
[2017-02-08 22:43:11,565] INFO Thread 2 (ConditionTest.java:48) - Thread 2 正在运行.....
java.lang.InterruptedException
[2017-02-08 22:43:13,566] INFO Thread 2 (ConditionTest.java:57) - Thread 2 发送一个 signal
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014)
[2017-02-08 22:43:13,566] INFO Thread 2 (ConditionTest.java:58) - Thread 2 发送 signal 结束
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2048)
[2017-02-08 22:43:13,567] INFO Thread 1 (ConditionTest.java:35) - Thread 1 获取一个 signal, 继续执行
at com.lami.tuomatuo.search.base.concurrent.aqs.ConditionTest$1.run(ConditionTest.java:31)
2.条件コンストラクターレベルの基本属性
主に条件キューのヘッドノードとテールノード(ヘッドノードとテールノードをここで初期化する必要はありません)
/** First node of condition queue */
/** Condition Queue 里面的头节点 */
private transient Node firstWaiter;
/** Last node of condition queue */
/** Condition Queue 里面的尾节点 */
private transient Node lastWaiter;
/** Creates a new {@code ConditionObject} instance */
/** 构造函数 */
public ConditionObject(){}
3.条件キューエンキューノードメソッドaddConditionWaiter
addConditionWaiterメソッドは、Condition.awaitを呼び出すときに現在のノードをノードにカプセル化し、それを条件キューに追加するために主に使用されます
条件キューに対する次の操作では同時実行性が考慮されていないことに注意してください(同期キューキューは同時操作をサポートしています)これはなぜですか?操作条件は、現在のスレッドがAQSの排他ロックを取得しているためです、したがって、同時実行性を考慮する必要はありません
/**
* Adds a new waiter to wait queue
* 将当前线程封装成一个 Node 节点 放入大 Condition Queue 里面
* 大家可以注意到, 下面对 Condition Queue 的操作都没考虑到 并发(Sync Queue 的队列是支持并发操作的), 这是为什么呢? 因为在进行操作 Condition 是当前的线程已经获取了AQS的独占锁, 所以不需要考虑并发的情况
* @return
*/
private Node addConditionWaiter(){
Node t = lastWaiter; // 1. Condition queue 的尾节点
// If lastWaiter is cancelled, clean out // 2.尾节点已经Cancel, 直接进行清除,
// 这里有1个问题, 1 何时出现t.waitStatus != Node.CONDITION -> 在对线程进行中断时 ConditionObject -> await -> checkInterruptWhileWaiting -> transferAfterCancelledWait "compareAndSetWaitStatus(node, Node.CONDITION, 0)" <- 导致这种情况一般是 线程中断或 await 超时
// 一个注意点: 当Condition进行 awiat 超时或被中断时, Condition里面的节点是没有被删除掉的, 需要其他 await 在将线程加入 Condition Queue 时调用addConditionWaiter而进而删除, 或 await 操作差不多结束时, 调用 "node.nextWaiter != null" 进行判断而删除 (PS: 通过 signal 进行唤醒时 node.nextWaiter 会被置空, 而中断和超时时不会)
if(t != null && t.waitStatus != Node.CONDITION){
unlinkCancelledWaiters(); // 3. 调用 unlinkCancelledWaiters 对 "waitStatus != Node.CONDITION" 的节点进行删除(在Condition里面的Node的waitStatus 要么是CONDITION(正常), 要么就是 0 (signal/timeout/interrupt))
t = lastWaiter; // 4. 获取最新的 lastWaiter
}
Node node = new Node(Thread.currentThread(), Node.CONDITION); // 5. 将线程封装成 node 准备放入 Condition Queue 里面
if(t == null){
firstWaiter = node; // 6 .Condition Queue 是空的
}else{
t.nextWaiter = node; // 7. 最加到 queue 尾部
}
lastWaiter = node; // 8. 重新赋值 lastWaiter
return node;
}
4.最初のノードメソッドdoSignalをウェイクアップする条件
ここでのウェイクアップとは、ノードを条件キューから同期キューに転送することを指します
/**
* 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
* @param first
*/
/**
* 唤醒 Condition Queue 里面的头节点, 注意这里的唤醒只是将 Node 从 Condition Queue 转到 Sync Queue 里面(这时的 Node 也还是能被 Interrupt)
*/
private void doSignal(Node first){
do{
if((firstWaiter = first.nextWaiter) == null){ // 1. 将 first.nextWaiter 赋值给 nextWaiter 为下次做准备
lastWaiter = null; // 2. 这时若 nextWaiter == null, 则说明 Condition 为空了, 所以直接置空 lastWaiter
}
first.nextWaiter = null; // 3. first.nextWaiter == null 是判断 Node 从 Condition queue 转移到 Sync Queue 里面是通过 signal 还是 timeout/interrupt
}while(!transferForSignal(first) && (first = firstWaiter) != null); // 4. 调用 transferForSignal将 first 转移到 Sync Queue 里面, 返回不成功的话, 将 firstWaiter 赋值给 first
}
5.すべてのノードをウェイクアップする条件メソッドdoSignalAll
/**
* Removes and transfers all nodes
* @param first (non-null) the first node on condition queue
*/
/**
* 唤醒 Condition Queue 里面的所有的节点
*/
private void doSignalAll(Node first){
lastWaiter = firstWaiter = null; // 1. 将 lastWaiter, firstWaiter 置空
do{
Node next = first.nextWaiter; // 2. 初始化下个换新的节点
first.nextWaiter = null; // 3. first.nextWaiter == null 是判断 Node 从 Condition queue 转移到 Sync Queue 里面是通过 signal 还是 timeout/interrupt
transferForSignal(first); // 4. 调用 transferForSignal将 first 转移到 Sync Queue 里面
first = next; // 5. 开始换新 next 节点
}while(first != null);
}
6.キャンセルノードを削除するための条件メソッドunlinkCancelledWaiters
通常のノードはシグナルによって起動され、条件キューから同期キューに転送されます。割り込みまたは待機タイムアウトが発生した場合、ノードの状態は直接変更され(条件から0に)、条件をクリーンアップせずに直接同期されますキュー内のノードなので、次の関数が必要です
/**
* http://czj4451.iteye.com/blog/1483264
*
* Unlinks cancelled waiter nodes from condition queue
* Called only while holding lock. This is called when
* cancellation occured 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 intot play when
* timeouts or cancellations all nodes rather than stoppping at a
* particular target to unlink all pointers to garbege nodes
* without requiring many re-traversals during cancellation
* storms
*/
/**
* 在 调用 addConditionWaiter 将线程放入 Condition Queue 里面时 或 awiat 方法获取 差不多结束时 进行清理 Condition queue 里面的因 timeout/interrupt 而还存在的节点
* 这个删除操作比较巧妙, 其中引入了 trail 节点, 可以理解为traverse整个 Condition Queue 时遇到的最后一个有效的节点
*/
private void unlinkCancelledWaiters(){
Node t = firstWaiter;
Node trail = null;
while(t != null){
Node next = t.nextWaiter; // 1. 先初始化 next 节点
if(t.waitStatus != Node.CONDITION){ // 2. 节点不有效, 在Condition Queue 里面 Node.waitStatus 只有可能是 CONDITION 或是 0(timeout/interrupt引起的)
t.nextWaiter = null; // 3. Node.nextWaiter 置空
if(trail == null){ // 4. 一次都没有遇到有效的节点
firstWaiter = next; // 5. 将 next 赋值给 firstWaiter(此时 next 可能也是无效的, 这只是一个临时处理)
}else{
trail.nextWaiter = next; // 6. next 赋值给 trail.nextWaiter, 这一步其实就是删除节点 t
}
if(next == null){ // 7. next == null 说明 已经 traverse 完了 Condition Queue
lastWaiter = trail;
}
}else{
trail = t; // 8. 将有效节点赋值给 trail
}
t = next;
}
}
これがキューノードの非常にデリケートな削除であることに疑いの余地はありません(主にノードトレール)トレイルノードは、条件キュー全体をトラバースしたときに遭遇した最後の有効なノードとして理解できます。
7.最初のノードメソッド信号をウェイクアップする条件
/**
* Moves the longest-waiting thread, if one exists, from the
* wait queue for this condition to the wait queue for the
* owning lock
*
* @throws IllegalMonitorStateException if{@link #isHeldExclusively()}
* returns {@code false}
*/
/**
* 将 Condition queue 的头节点转移到 Sync Queue 里面
* 在进行调用 signal 时, 当前的线程必须获取了 独占的锁
*/
@Override
public void signal() {
if(!isHeldExclusively()){ // 1. 判断当前的线程是否已经获取 独占锁
throw new IllegalMonitorStateException();
}
Node first = firstWaiter;
if(first != null){
doSignal(first); // 2. 调用 doSignal 进行转移
}
}
上記のインタビューの質問に対する回答は、ドキュメントノートにまとめられています。また、インタビューに関するいくつかの情報と、インタビューZhentiの最新2020コレクションのメーカーの一部(両方ともスクリーンショットの小さな部分を文書化しています)をまとめました。クリックして信号を入力する必要があります:CSDN。
8.条件はすべてのノードをウェイクアップしますsignalAll
/**
* Moves all threads from the wait queue for this condition to
* the wait queue for the owning lock
*
* @throws IllegalMonitorStateException if {@link #isHeldExclusively()}
* return {@code false}
*/
/**
* 将 Condition Queue 里面的节点都转移到 Sync Queue 里面
*/
public final void signalAll(){
if(!isHeldExclusively()){
throw new IllegalMonitorStateException();
}
Node first = firstWaiter;
if(first != null){
doSignalAll(first);
}
}
9.条件がロックを解放し、メソッドawaitUninterruptiblyを待つ
awaitUninterruptiblyメソッドは、割り込みに応答しないメソッドです。
プロセス全体
- 現在のスレッドをノードとしてカプセル化し、条件に追加します
- 現在のスレッドが所有する排他ロックが失われました
- 排他ロックを取得した他のスレッドのウェイクアップを待機し、条件キューから同期キューにウェイクアップしてから、排他ロックを取得します
- ロックが最終的に取得された後、スレッドが起動する方法(シグナル/割り込み)に従って処理されます。
/**
* Implements uninterruptible condition wait
* <li>
* Save lock state return by {@link #getState()}
* </li>
*
* <li>
* Invoke {@link #release(int)} with saved state as argument,
* throwing IllegalMonitoringStateException if it fails
* Block until signalled
* Reacquire by invoking specified version of
* {@link #acquire(int)} with saved state as argument
* </li>
*/
/**
* 不响应线程中断的方式进行 await
*/
public final void awaitUninterruptibly(){
Node node = addConditionWaiter(); // 1. 将当前线程封装成一个 Node 放入 Condition Queue 里面
int savedState = fullyRelease(node); // 2. 释放当前线程所获取的所有的独占锁(PS: 独占的锁支持重入), 等等, 为什么要释放呢? 以为你调用 awaitUninterruptibly 方法的前提就是你已经获取了 独占锁
boolean interrupted = false; // 3. 线程中断标识
while(!isOnSyncQueue(node)){ // 4. 这里是一个 while loop, 调用 isOnSyncQueue 判断当前的 Node 是否已经被转移到 Sync Queue 里面
LockSupport.park(this); // 5. 若当前 node 不在 sync queue 里面, 则先 block 一下等待其他线程调用 signal 进行唤醒; (这里若有其他线程对当前线程进行 中断的换, 也能进行唤醒)
if(Thread.interrupted()){ // 6. 判断这是唤醒是 signal 还是 interrupted(Thread.interrupted()会清楚线程的中断标记, 但没事, 我们有步骤7中的interrupted进行记录)
interrupted = true; // 7. 说明这次唤醒是被中断而唤醒的,这个标记若是true的话, 在 awiat 离开时还要 自己中断一下(selfInterrupt), 其他的函数可能需要线程的中断标识
}
}
if(acquireQueued(node, savedState) || interrupted){ // 8. acquireQueued 返回 true 说明线程在 block 的过程中式被 inetrrupt 过(其实 acquireQueued 返回 true 也有可能其中有一次唤醒是 通过 signal)
selfInterrupt(); // 9. 自我中断, 外面的线程可以通过这个标识知道, 整个 awaitUninterruptibly 运行过程中 是否被中断过
}
}
10.条件割り込みマーク
次の2つは、awaitXXXメソッドが呼び出されたときにスレッドが中断されたかどうかを追跡するために使用されます。
主な違いは
1です。REINTERRUPT:シグナルの後でスレッドが中断されたことを示します(REINTERRUPT =再中断、最終的にもう一度中断するとselfInterruptが呼び出されます)
2. THROW_IE :シグナルを受信する前に中断されたことを表し、直接例外をスローします(Throw_IE =内部例外をスロー)
/**
* 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
*/
/**
* 下面两个是用于追踪 调用 awaitXXX 方法时线程有没有被中断过
* 主要的区别是
* REINTERRUPT: 代表线程是在 signal 后被中断的 (REINTERRUPT = re-interrupt 再次中断 最后会调用 selfInterrupt)
* THROW_IE: 代表在接受 signal 前被中断的, 则直接抛出异常 (Throw_IE = throw inner exception)
*/
/** Mode meaning to reinterrupt on exit from wait */
/** 在离开 awaitXX方法, 退出前再次 自我中断 (调用 selfInterrupt)*/
private static final int REINTERRUPT = 1;
/** Mode meaning to throw InterruptedException on exit from wait */
/** 在离开 awaitXX方法, 退出前再次, 以为在 接受 signal 前被中断, 所以需要抛出异常 */
private static final int THROW_IE = -1;
11.条件割り込み処理方法
このメソッドは主に、awaitXXメソッドのウェイクアップが割り込みによるものかどうかを確認するためのもので、割り込みによるものである場合はノードに転送します。
/**
* Checks for interrupt, returning THROW_IE if interrupted
* before signalled, REINTERRUPT if after signalled, or
* 0 if not interrupted
*/
/**
* 检查 在 awaitXX 方法中的这次唤醒是否是中断引起的
* 若是中断引起的, 则将 Node 从 Condition Queue 转移到 Sync Queue 里面
* 返回值的区别:
* 0: 此次唤醒是通过 signal -> LockSupport.unpark
* THROW_IE: 此次的唤醒是通过 interrupt, 并且 在 接受 signal 之前
* REINTERRUPT: 线程的唤醒是 接受过 signal 而后又被中断
*/
private int checkInterruptWhileWaiting(Node node){
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) : 0;
}
12.条件割り込み処理メソッドreportInterruptAfterWait
/**
* Throws InterruptedException, reinterrupts current thread, or
* does nothing, depending on mode
*/
/**
* 这个方法是在 awaitXX 方法离开前调用的, 主要是根据
* interrupMode 判断是抛出异常, 还是自我再中断一下
*/
private void reportInterruptAfterWait(int interrupMode) throws InterruptedException{
if(interrupMode == THROW_IE){
throw new InterruptedException();
}
else if(interrupMode == REINTERRUPT){
selfInterrupt();
}
}
13.ロックを解除して待機するための条件メソッド
awaitこのメソッドは、割り込み要求に応答し、割り込み要求を受信した後、ノードを条件キューから同期キューに転送します
/**
* Implements interruptible condition wait
*
* <li>
* If current thread is interrupted, throw InterruptedException
* Save lock state returned by {@link #getState()}
* Invoke {@link #release(int)} with saved state as argument,
* throwing IllegalMonitorStateException if it fails
* Blocking until signalled or interrupted
* Reacquire by invoking specifized version of
* {@link #acquire(int)} with saved state as argument.
* If interrupted while blocked in step 4, throw InterruptedException
* </li>
*
* @throws InterruptedException
*/
/**
* 支持 InterruptedException 的 await <- 注意这里即使是线程被中断,
* 还是需要获取了独占的锁后, 再 调用 lock.unlock 进行释放锁
*/
@Override
public final void await() throws InterruptedException {
if(Thread.interrupted()){ // 1. 判断线程是否中断
throw new InterruptedException();
}
Node node = addConditionWaiter(); // 2. 将线程封装成一个 Node 放到 Condition Queue 里面, 其中可能有些清理工作
int savedState = fullyRelease(node); // 3. 释放当前线程所获取的所有的锁 (PS: 调用 await 方法时, 当前线程是必须已经获取了独占的锁)
int interruptMode = 0;
while(!isOnSyncQueue(node)){ // 4. 判断当前线程是否在 Sync Queue 里面(这里 Node 从 Condtion Queue 里面转移到 Sync Queue 里面有两种可能 (1) 其他线程调用 signal 进行转移 (2) 当前线程被中断而进行Node的转移(就在checkInterruptWhileWaiting里面进行转移))
LockSupport.park(this); // 5. 当前线程没在 Sync Queue 里面, 则进行 block
if((interruptMode = checkInterruptWhileWaiting(node)) != 0){ // 6. 判断此次线程的唤醒是否因为线程被中断, 若是被中断, 则会在checkInterruptWhileWaiting的transferAfterCancelledWait 进行节点的转移; 返回值 interruptMode != 0
break; // 说明此是通过线程中断的方式进行唤醒, 并且已经进行了 node 的转移, 转移到 Sync Queue 里面
}
}
if(acquireQueued(node, savedState) && interruptMode != THROW_IE){ // 7. 调用 acquireQueued在 Sync Queue 里面进行 独占锁的获取, 返回值表明在获取的过程中有没有被中断过
interruptMode = REINTERRUPT;
}
if(node.nextWaiter != null){ // clean up if cancelled // 8. 通过 "node.nextWaiter != null" 判断 线程的唤醒是中断还是 signal, 因为通过中断唤醒的话, 此刻代表线程的 Node 在 Condition Queue 与 Sync Queue 里面都会存在
unlinkCancelledWaiters(); // 9. 进行 cancelled 节点的清除
}
if(interruptMode != 0){ // 10. "interruptMode != 0" 代表通过中断的方式唤醒线程
reportInterruptAfterWait(interruptMode); // 11. 根据 interruptMode 的类型决定是抛出异常, 还是自己再中断一下
}
}
14.ロックを解放してawaitNanosを待機するための条件メソッド
awaitNanosにはタイムアウト機能があり、中断やタイムアウトに関係なく、中断に応答する機能があり、ノードを条件キューから同期キューに転送します
/**
* Impelemnts timed condition wait
*
* <li>
* If current thread is interrupted, throw InterruptedException
* Save lock state returned by {@link #getState()}
* Invoke {@link #release(int)} with saved state as argument,
* throwing IllegalMonitorStateException if it fails
* Block until aignalled, interrupted, or timed out
* Reacquire by invoking specified version of
* {@link #acquire(int)} with saved state as argument
* If interrupted while blocked in step 4, throw InterruptedException
* </li>
*/
/**
* 所有 awaitXX 方法其实就是
* 0. 将当前的线程封装成 Node 加入到 Condition 里面
* 1. 丢到当前线程所拥有的 独占锁,
* 2. 等待 其他获取 独占锁的线程的唤醒, 唤醒从 Condition Queue 到 Sync Queue 里面, 进而获取 独占锁
* 3. 最后获取 lock 之后, 在根据线程唤醒的方式(signal/interrupt) 进行处理
* 4. 最后还是需要调用 lock./unlock 进行释放锁
*/
@Override
public final long awaitNanos(long nanosTimeout) throws InterruptedException {
if(Thread.interrupted()){ // 1. 判断线程是否中断
throw new InterruptedException();
}
Node node = addConditionWaiter(); // 2. 将线程封装成一个 Node 放到 Condition Queue 里面, 其中可能有些清理工作
int savedState = fullyRelease(node); // 3. 释放当前线程所获取的所有的锁 (PS: 调用 await 方法时, 当前线程是必须已经获取了独占的锁)
final long deadline = System.nanoTime() + nanosTimeout; // 4. 计算 wait 的截止时间
int interruptMode = 0;
while(!isOnSyncQueue(node)){ // 5. 判断当前线程是否在 Sync Queue 里面(这里 Node 从 Condtion Queue 里面转移到 Sync Queue 里面有两种可能 (1) 其他线程调用 signal 进行转移 (2) 当前线程被中断而进行Node的转移(就在checkInterruptWhileWaiting里面进行转移))
if(nanosTimeout <= 0L){ // 6. 等待时间超时(这里的 nanosTimeout 是有可能 < 0),
transferAfterCancelledWait(node); // 7. 调用 transferAfterCancelledWait 将 Node 从 Condition 转移到 Sync Queue 里面
break;
}
if(nanosTimeout >= spinForTimeoutThreshold){ // 8. 当剩余时间 < spinForTimeoutThreshold, 其实函数 spin 比用 LockSupport.parkNanos 更高效
LockSupport.parkNanos(this, nanosTimeout); // 9. 进行线程的 block
}
if((interruptMode = checkInterruptWhileWaiting(node)) != 0){ // 10. 判断此次线程的唤醒是否因为线程被中断, 若是被中断, 则会在checkInterruptWhileWaiting的transferAfterCancelledWait 进行节点的转移; 返回值 interruptMode != 0
break; // 说明此是通过线程中断的方式进行唤醒, 并且已经进行了 node 的转移, 转移到 Sync Queue 里面
}
nanosTimeout = deadline - System.nanoTime(); // 11. 计算剩余时间
}
if(acquireQueued(node, savedState) && interruptMode != THROW_IE){ // 12. 调用 acquireQueued在 Sync Queue 里面进行 独占锁的获取, 返回值表明在获取的过程中有没有被中断过
interruptMode = REINTERRUPT;
}
if(node.nextWaiter != null){ // 13. 通过 "node.nextWaiter != null" 判断 线程的唤醒是中断还是 signal, 因为通过中断唤醒的话, 此刻代表线程的 Node 在 Condition Queue 与 Sync Queue 里面都会存在
unlinkCancelledWaiters(); // 14. 进行 cancelled 节点的清除
}
if(interruptMode != 0){ // 15. "interruptMode != 0" 代表通过中断的方式唤醒线程
reportInterruptAfterWait(interruptMode); // 16. 根据 interruptMode 的类型决定是抛出异常, 还是自己再中断一下
}
return deadline - System.nanoTime(); // 17 这个返回值代表是 通过 signal 还是 超时
}
15.ロックを解放して待機するawaitUntilメソッドの条件
/**
* Implements absolute timed condition wait
* <li>
* If current thread is interrupted, throw InterruptedException
* Save lock state returned by {@link #getState()}
* Invoke {@link #release(int)} with saved state as argument,
* throwing IllegalMonitorStateException if it fails
* Block until signalled, interrupted, or timed out
* Reacquire by invoking specialized version of
* {@link #acquire(int)} with saved state as argument
* if interrupted while blocked in step 4, throw InterruptedException
* If timeed out while blocked in step 4, return false, else true
* </li>
*/
/**
* 所有 awaitXX 方法其实就是
* 0. 将当前的线程封装成 Node 加入到 Condition 里面
* 1. 丢到当前线程所拥有的 独占锁,
* 2. 等待 其他获取 独占锁的线程的唤醒, 唤醒从 Condition Queue 到 Sync Queue 里面, 进而获取 独占锁
* 3. 最后获取 lock 之后, 在根据线程唤醒的方式(signal/interrupt) 进行处理
* 4. 最后还是需要调用 lock./unlock 进行释放锁
*
* awaitUntil 和 awaitNanos 差不多
*/
@Override
public boolean awaitUntil(Date deadline) throws InterruptedException {
long abstime = deadline.getTime(); // 1. 判断线程是否中断
if(Thread.interrupted()){
throw new InterruptedException();
}
Node node = addConditionWaiter(); // 2. 将线程封装成一个 Node 放到 Condition Queue 里面, 其中可能有些清理工作
int savedState = fullyRelease(node); // 3. 释放当前线程所获取的所有的锁 (PS: 调用 await 方法时, 当前线程是必须已经获取了独占的锁)
boolean timeout = false;
int interruptMode = 0;
while(!isOnSyncQueue(node)){ // 4. 判断当前线程是否在 Sync Queue 里面(这里 Node 从 Condtion Queue 里面转移到 Sync Queue 里面有两种可能 (1) 其他线程调用 signal 进行转移 (2) 当前线程被中断而进行Node的转移(就在checkInterruptWhileWaiting里面进行转移))
if(System.currentTimeMillis() > abstime){ // 5. 计算是否超时
timeout = transferAfterCancelledWait(node); // 6. 调用 transferAfterCancelledWait 将 Node 从 Condition 转移到 Sync Queue 里面
break;
}
LockSupport.parkUntil(this, abstime); // 7. 进行 线程的阻塞
if((interruptMode = checkInterruptWhileWaiting(node)) != 0){ // 8. 判断此次线程的唤醒是否因为线程被中断, 若是被中断, 则会在checkInterruptWhileWaiting的transferAfterCancelledWait 进行节点的转移; 返回值 interruptMode != 0
break; // 说明此是通过线程中断的方式进行唤醒, 并且已经进行了 node 的转移, 转移到 Sync Queue 里面
}
}
if(acquireQueued(node, savedState) && interruptMode != THROW_IE){ // 9. 调用 acquireQueued在 Sync Queue 里面进行 独占锁的获取, 返回值表明在获取的过程中有没有被中断过
interruptMode = REINTERRUPT;
}
if(node.nextWaiter != null){ // 10. 通过 "node.nextWaiter != null" 判断 线程的唤醒是中断还是 signal, 因为通过中断唤醒的话, 此刻代表线程的 Node 在 Condition Queue 与 Sync Queue 里面都会存在
unlinkCancelledWaiters(); // 11. 进行 cancelled 节点的清除
}
if(interruptMode != 0){ // 12. "interruptMode != 0" 代表通过中断的方式唤醒线程
reportInterruptAfterWait(interruptMode); // 13. 根据 interruptMode 的类型决定是抛出异常, 还是自己再中断一下
}
return !timeout; // 13. 返回是否通过 interrupt 进行线程的唤醒
}
16.状態の計測方法
/**
* Returns true if this condition was created by the given
* synchronization object
*/
/**判断当前 condition 是否获取 独占锁 */
final boolean isOwnedBy(KAbstractOwnableSynchronizer sync){
return sync == KAbstractQueuedSynchronizer.this;
}
/**
* Quires whether any threads are waiting on this condition
* Implements {@link KAbstractOwnableSynchronizer#"hasWaiters(ConditionObject)}
*
* @return {@code true} if there are any waiting threads
* @throws IllegalMonitorStateException if {@link #isHeldExclusively()}
* returns {@code false}
*/
/**
* 查看 Condition Queue 里面是否有 CONDITION 的节点
*/
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 KAbstractOwnableSynchronizer#"getWaitQueueLength()}
*
* @return the estimated number of waiting threads
* @throws IllegalMonitorStateException if {@link #isHeldExclusively()}
* return {@code false}
*/
/**
* 获取 Condition queue 里面的 CONDITION 的节点的个数
*/
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
* waiting on this Condition
* Implements {@link KAbstractOwnableSynchronizer#'getWaitingThreads}
*
* @return the collection of thread
* @throws IllegalMonitorStateException if {@link #isHeldExclusively()}
* returns {@code false}
*/
/**
* 获取 等待的线程
*/
protected final Collection<Thread> getWaitingThreads(){
if(!isHeldExclusively()){
throw new IllegalMonitorStateException();
}
ArrayList<Thread> list = new ArrayList<>();
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;
}
これでこの記事の終わりです!
注意してください、迷わないでください!この記事があなたに役立つなら、好きで、サポートすることを忘れないでください!
上記のインタビューの質問に対する回答は、ドキュメントノートにまとめられています。また、インタビューに関するいくつかの情報と、インタビューZhentiの最新2020コレクションのメーカーの一部(両方ともスクリーンショットの小さな部分を文書化しています)をまとめました。クリックして信号を入力する必要があります:CSDN。
皆様のお役に立てれば幸いです。