王者并发课-铂金6:青出于蓝-Condition如何把等待与通知玩出新花样
2022/6/13 23:20:55
本文主要是介绍王者并发课-铂金6:青出于蓝-Condition如何把等待与通知玩出新花样,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
欢迎来到《王者并发课》,本文是该系列文章中的第19篇。
在上一篇文章中,我们介绍了阻塞队列。如果你阅读过它的源码,那么你一定会注意到其中的condition
变量以及它在读写队列时的使用。所以,本文就为你介绍Condition的来龙去脉和它的用法。
在前面的系列文章中,我们多次提到过synchronized
关键字,相信你已经对它的用法了如于心。在多线程协作时,有两个另外的关键字经常和synchronized
一同出现,它们相互配合,就是wait
和notify
,比如下面的这段代码:
public class CountingSemaphore { private int signals = 0; public synchronized void take() { this.signals++; this.notify(); // 发送通知 } public synchronized void release() throws InterruptedException { while (this.signals == 0) this.wait(); // 释放锁,进入等待 This.signals--; } }
synchronized
是Java的原生同步工具,wait
和notify
是它的原生搭档。然而,在铂金系列中,我们已经开始了Lock接口和它的一些实现,比如可重入锁ReentrantLock等。相比于synchronized
,JUC所封装的这些锁工具在功能上要丰富得多,也更加容易使用。所以,相应地配套自然也要跟上,于是Condition就应运而生。
比如在上文的阻塞队列中,Condition就已经闪亮登场:
public class LinkedBlockingQueue < E > extends AbstractQueue < E > implements BlockingQueue < E > , java.io.Serializable { ...省略源码若干 // 定义Condition // 注意,这里定义两个Condition对象,用于唤醒不同的线程 private final Condition notEmpty = takeLock.newCondition(); private final Condition notFull = putLock.newCondition(); public E take() throws InterruptedException { E x; int c = -1; final AtomicInteger count = this.count; final ReentrantLock takeLock = this.takeLock; takeLock.lockInterruptibly(); try { while (count.get() == 0) { // 进入等待 notEmpty.await(); } x = dequeue(); c = count.getAndDecrement(); if (c > 1) notEmpty.signal(); } finally { takeLock.unlock(); } if (c == capacity) signalNotFull(); return x; } private void signalNotEmpty() { final ReentrantLock takeLock = this.takeLock; takeLock.lock(); try { // 发送唤醒信号 notEmpty.signal(); } finally { takeLock.unlock(); } } ...省略源码若干 }
从功能定位上说,作为Lock的配套工具,Condition是wait
、notify
和notifyAll
增强版本,wait
和notify
有的能力它都有,wait
和notify
没有的能力它也有。
JUC中的Condition是以接口的形式出现,并定义了一些核心方法:
await()
:让当前线程进入等待,直到收到信号或者被中断;await(long time, TimeUnit unit)
:让当前线程进入等待,直到收到信号或者被中断,或者到达指定的等待超时时间;awaitNanos(long nanosTimeout)
:让当前线程进入等待,直到收到信号或者被中断,或者到达指定的等待超时时间,只是在时间单位上和上一个方法有所区别;awaitUninterruptibly()
:让当前线程进入等待,直到收到信号。注意,这个方法对中断是不敏感的;awaitUntil(Date deadline)
:让当前线程进入等待,直到收到信号或者被中断,或者到达截止时间;signal()
:随机唤醒一个线程;signalAll()
:唤醒所有等待的线程。
从Condition的核心方法中可以看到,相较于原生的通知与等待,它的能力明显增强了很多,比如awaitUninterruptibly()
和awaitUntil()
。另外,Condition竟然是可以唤醒指定线程的,这就很有意思。
作为接口,我们并不需要手动实现Condition,JUC已经提供了相关的实现,你可以在ReentrantLock中直接使用它。相关的类、接口之间的关系如下所示:
小结
以上就是关于Condition的全部内容。Condition并不复杂,它是JUC中Lock的配套,在理解时要结合原生的wait
和notify
去理解。关于Condition与它们之间的详细区别,已经都在下面的表格里:
对比项 | Object's Monitor methods | Condition |
---|---|---|
前置条件 | 获取对象的锁 | 调用Lock获取锁,调用lock.newCondition()获取Condition对象 |
调用方式 | 直接调用,如object.wait() | 直接调用,如condition.await() |
等待队列个数 | 一个 | 多个 |
当前线程释放锁并进入等待状态 | ✔︎ | ✔︎ |
当前线程释放锁并进入等待状态,在等待时不响应中断 | ✘ | ✔︎ |
当前线程释放锁并进入超时等待 | ✔︎ | ✔︎ |
当前线程释放锁并进入等待到未来某个时刻 | ✘ | ✔︎ |
唤醒等待队列中的某一个线程 | ✔︎ | ✔︎ |
唤醒等待队列中的全部线程 | ✔︎ | ✔︎ |
理解表格中的各项差异,不要死记硬背,而是要基于Condition接口中定义的方法,从关键处理解它们的不同。
正文到此结束,恭喜你又上了一颗星✨
夫子的试炼
- 编写代码使用Condition唤醒指定线程。
延伸阅读与参考资料
- 小结表格中的内容由网络图片提取,未能找到原始出处,知道的请评论告知,感谢!
- 《王者并发课》大纲与更新进度总览
关于作者
从业近十年,先后从事敏捷与DevOps咨询、Tech Leader和管理等工作,对分布式高并发架构有丰富的实战经验。热衷于技术分享和特定领域书籍翻译,掘金小册《高并发秒杀的设计精要与实现》作者。
关注公众号【MetaThoughts】,及时获取文章更新和文稿。
如果本文对你有帮助,欢迎点赞、关注、监督,我们一起从青铜到王者。
这篇关于王者并发课-铂金6:青出于蓝-Condition如何把等待与通知玩出新花样的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享