Lock 接口提供了方法 Condition newCondition(); 用于获取对应锁的条件, 可以在这个条件对象上调用监视器方法
可以理解为, 原本借助于 synchronized 关键字以及锁对象, 配备了一个监视器
而显式锁 Lock 与 Condition 则针对于一个锁对象, 提供了多个监视器
尽管是提供了多个监视器, 但是需要记住, 是 Lock 接口提供方法才能够获取到条件对象, 所以这些条件对象仍旧是绑定到某一把锁上的
我相信, 只要理解了监视器的概念, 对于 Condition 理解起来是不会存在任何难度的, 因为本身就是另外一种实现方式
从下图可以直观的感受到 Condition 是作为 Object 监视器方法的另外实现
wait 在这边叫做 await
notify 在这边叫做 signal
等待
await
在监视器上等待
void await() throws InterruptedException; , 看得出来, 此方法是支持中断的
除非发生以下事件, 否则将会持续等待
其他某个线程调用此 Condition 的 signal() 方法, 并且碰巧唤醒的是该线程
其他某个线程调用此 Condition 的 signalAll() 方法;
其他某个线程中断当前线程
- "虚假唤醒"
- awaitUninterruptibly
不支持中断的 await 方法, void awaitUninterruptibly();
从 await() 的介绍中看得出来, 除非发生提到的四种情况, 否则将会是等待状态
而 void awaitUninterruptibly(); 则是 await() 的不可中断版本, 也就是只会有三种情况会跳出等待
其他某个线程调用此 Condition 的 signal() 方法, 并且碰巧唤醒的是该线程
其他某个线程调用此 Condition 的 signalAll() 方法;
其他某个线程中断当前线程
- "虚假唤醒"
- awaitNanos
- long awaitNanos(long nanosTimeout) throws InterruptedException;
支持设置超时的等待, 参数为等待的纳秒的 long 型数值
他在基于 await 的前提下, 新增加了超时跳出, 否则将会一直等待, 他的跳出条件如下
其他某个线程调用此 Condition 的 signal() 方法, 并且碰巧唤醒的是该线程
其他某个线程调用此 Condition 的 signalAll() 方法;
其他某个线程中断当前线程
"虚假唤醒"
已超过指定的等待时间 (新增)
返回值
需要注意的是此方法的返回值, 是一个 long
我们设置了一个超时时间, 那么到底用了多少秒, 还剩下多少秒? 这个返回值就是这意思:
(nanosTimeout - 实际花费时间) 的估算值, 如果小于等于 0, 表示没有剩余时长; 如果大于 0, 可以用来确定如果等待返回了是否还需要继续等待?
比如, 你在等朋友 A,A 说你等我一小时, 于是你去睡觉了, 结果你睡了半小时就被叫醒了, 那么跟你本来要等的时间还差半小时
那还要不要继续等了? 还是一定要等到总共一小时呢?
典型用法 (来自 API):
- boolean aMethod(long timeout, TimeUnit unit) {
- long nanos = unit.toNanos(timeout);
- lock.lock();
- try {
- while (!conditionBeingWaitedFor()) {
- if (nanos <= 0L)
- return false;
- nanos = theCondition.awaitNanos(nanos);
- }
- // ...
- } finally {
- lock.unlock();
- }
- }
上面的方法中, 如果条件仍旧不满足, 但是等待结束了 (也就是等待了足够多的时间了), 直接返回 false; 否则将会继续执行, 直到等到最后一刻
ps: 这种代码风格也就 JDK 常写, 否者这种 if 形式, 估计要被项目经理骂
- await(long time, TimeUnit unit)
- boolean await(long time, TimeUnit unit) throws InterruptedException;
此方法也是设置超时时长的等待方法, 第一个参数为时长个数, 第二个参数为第一个参数的单位
他相当于 awaitNanos 方法的封装 (此处是逻辑上看起来, 而不是说就是这个方法的封装)
awaitNanos(unit.toNanos(time))> 0
所以返回类型为 boolean, 显然 true 表示没有等待足够的时间;,false 表示等待了足够时间, 也就是等待超时
- awaitUntil
- boolean awaitUntil(Date deadline) throws InterruptedException;
awaitUntil 类似于 await(long time, TimeUnit unit), 只不过不是设置超时时长, 而是设置截止日期
逻辑上可以把他们理解为一回事, 如果没有等待足够时长, 那么返回 true; 如果等待超时那么返回 false
常用的逻辑 (来自 API)
- boolean aMethod(Date deadline) {
- boolean stillWaiting = true;
- lock.lock();
- try {
- while (!conditionBeingWaitedFor()) {
- if (!stillWaiting)
- return false;
- stillWaiting = theCondition.awaitUntil(deadline);
- }
- // ...
- } finally {
- lock.unlock();
- }
- }
上面的代码中, 如果等待了足够的时长 (等待超时), 那么就会返回 false; 如果还有剩余时间, 继续等待
普通的 await() 方法和 awaitUninterruptibly 都是直白的等待, 一个支持中断, 一个不支持中断
有超时设置的三个方法:
awaitNanos,await(long time, TimeUnit unit),awaitUntil
都是在 await() 的基础上对超时时长或者截止日期的设置使用
不过这几个方法会返回剩余的超时时长或者使用 boolean 指示是否还有剩余
所以如果条件不满足, 如果还没等够时间, 可以控制继续等待或者退出
而对于 Object 提供的 wait 方法, 就不能做到这么灵活的控制, 要么就条件不满足继续等待, 要么醒来后继续做别的事情, 没办法相对准确的控制 "必须要等待一定的时长", 因为如果 wait(一小时),5 分钟后, 被唤醒了, 这个用掉了的五分钟 (或者说还剩余 55 分钟, 是不知道的)
唤醒
关于唤醒有与 Object 提供的类似的两个方法, 他们的逻辑含义也是一样的, 唤醒一个或者唤醒所有, 概念上并没有太多需要注意的事情
- void signal();
- void signalAll();
总结
Condition 本身就是 Object 中监视器概念的另外实现, 所以监视器方法调用, 也需要持有锁, 也就是说:
调用 Condition 的 await() 和 signal() 等方法, 都必须在 lock 保护之内, 就是说必须在 lock.lock() 和 lock.unlock 之间才可以使用
await 系列方法相对于 Object 提供了更加灵活的使用形式, signal 和 signalAll 的逻辑含义可以认为完全一致
另外需要注意 await 方法与 Object 中的 wait 一样, 都会释放当前持有的锁, 所以醒来之后, 会需要重新获取锁
来源: https://www.cnblogs.com/noteless/p/10493944.html