Java并发:Condition接口

Condition 接口与 Lock 配合实现了等待 / 通知模式,这个和 Object 的监视器方法(wait、notify、notifyAll 等方法)一样,都是实现了等待 / 通知模式,但这两者在使用的方式以及功能特性上还是有差别的。

Object 的监视器方法与 Condition 接口的对比
对比项 Object Monitor Methods Condition
前置条件 获取对象的锁

调用Lock.lock()获取锁

调用Lock.newCondition()获取Condition对象

调用方式 直接调用,如:Object.wait() 直接调用,如:condition.await()
等待队列个数 一个 多个
当前线程释放锁并进入等待状态 支持 支持

当前线程释放锁并进入等待状态,

在等待状态中不响应中断

不支持 支持
当前线程释放锁并进入等待超时状态 支持 支持
当前现场释放锁并进入等待状态到将来某个时间 不支持 支持
唤醒等待队列中的一个线程 支持 支持
唤醒等待队列中的所有线程 支持 支持

在 Object 监视模型中,一个对象有一个同步队列和一个等待队列,而 AQS 拥有一个同步队列和多个等待队列。

Object 的监视模型:

Condition 的同步队列与等待队列:

Condition 部分方法以及描述:

方法名称 描述
void await() throws
InterruptedException

当前线程进入等待状态,直到被通知(signal)或中断,

当前线程才会进入运行状态且从此await方法返回(表明

该线程已经获取了Condition对象对应的锁),包括:

其他线程(调用了interrupt()方法中断了当前线程)调用

该 Condition 的 signal() 或 signalAll()方法,而当前线

程被选中唤醒

void awaitUninterruptibly()
当前线程进入等待状态,知道被通知,对中断不敏感
long awaitNanos(long nanosTimeout)
throws InterruptedException

当前线程进入等待状态直到被通知、中断或者是超时,

返回值代表剩余的时间,如果在nanosTimeout纳秒

之前被唤醒,返回值是(nanosTimeout - 实际耗时)。

若返回的是0或者负数,超时了。

boolean awaitUntil(Date deadline)

当前线程进入等待状态直到被通知、中断或者是到某个

时间。没有到指定时间就被通知,返回true;否则,

返回false

void signal()

唤醒一个等待在Condition上的线程,该线程从等待方法

返回前必须获得与Condition相关的锁

void signalAll()

唤醒所有等待在Condition上的线程,能够从等待方法

返回的线程必须获得与Condition相关联的锁

Condition 的实现

① 等待队列

前面的 Condition 的同步队列与等待队列,队列中的每个节点都包含一个线程引用,该线程就是在Condition对象上等待的线程,如果一个线程调用Condition.await()方法,那么该线程将会释放锁、构造成节点加入等待队列进入等待状态。同步队列和等待队列的节点类型都是AQS中的静态内部类Node。Condition是AQS中的静态内部类。

② 等待

调用Condition的await方法,会使当前线程进入等待队列并释放锁,同时线程状态变成等待状态。当从await方法返回时,当前线程一定获取了Condition相关联的锁。

 1     public final void await() throws InterruptedException {
 2             if (Thread.interrupted())
 3                 throw new InterruptedException();
 4             Node node = addConditionWaiter();    // 当前线程加入等待队列
 5             int savedState = fullyRelease(node);  // 释放同步状态,也就是释放锁
 6             int interruptMode = 0;
 7             while (!isOnSyncQueue(node)) {
 8                 LockSupport.park(this);
 9                 if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
10                     break;
11             }
12             if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
13                 interruptMode = REINTERRUPT;
14             if (node.nextWaiter != null) // 线程被取消的话要清除节点
15                 unlinkCancelledWaiters();
16             if (interruptMode != 0)
17                 reportInterruptAfterWait(interruptMode);
18         }

如果队列的角度来看await()方法,当调用await()方法时,相当于把同步队列的首节点(获取了锁的节点)移动到Condition的等待队列中,实际上是通过addConditionWaiter()方法把当前线程构造成一个新的节点并将其加入等待队列中。

③ 通知

调用该方法的前置条件是要获取了锁,方法首先用isHeldExclusively()检查,接着获取等待队列的首节点,将其移动到同步队列并使用LockSupport工具类唤醒节点中的线程。

1     public final void signal() {
2             if (!isHeldExclusively())
3                 throw new IllegalMonitorStateException();
4             Node first = firstWaiter;
5             if (first != null)
6                 doSignal(first);
7         }
1     private void doSignal(Node first) {
2             do {
3                 if ( (firstWaiter = first.nextWaiter) == null)
4                     lastWaiter = null;
5                 first.nextWaiter = null;
6             } while (!transferForSignal(first) &&
7                      (first = firstWaiter) != null);
8     }
 1     final boolean transferForSignal(Node node) {
 2         /*
 3          * 如果无法更改waitStatus,则该节点会被取消
 4          */
 5         if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
 6             return false;
 7 
 8         /*
 9          * 拼接到队列上并尝试设置前任的waitStatus以指示线程(可能)正在等待。
10          * 如果取消或尝试设置waitStatus失败,请唤醒以重新同步
11          * (在这种情况下,waitStatus可能是暂时性且无害的错误)
12          */
13         Node p = enq(node);
14         int ws = p.waitStatus;
15         if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
16             LockSupport.unpark(node.thread);
17         return true;
18     }

节点从等待队列移动到同步队列:

猜你喜欢

转载自www.cnblogs.com/magic-sea/p/11594861.html
今日推荐