Java中的等待通知机制
1|0Java中的等待通知机制
1|1综述
在Java中, 等待-通知机制是多线程编程中的一种同步机制, 主要用于线程之间的协调. 常见于广义生产者-消费者模式应用场景. 因为应用相当广泛, 又没有严格意义上的生产者-消费者模式那么难, 所以本文将单独整理一篇, 讨论这一类场景.
1|2API和概念介绍
当前, 可以实现等待通知机制的api有两组, 一组以 synchronized/wait/notify 为代表, 另一组则以 Lock/Condition 为代表. 其中 synchronized 和 Lock 属于基础的锁功能, 此处不再额外介绍. 各组其余的api的功能简述如下.
wait等api由object类提供, 也就是说, 任何可以作为锁用于synchronized的Object子类实例都可以提供这几个方法.
- wait(): 让当前线程进入等待状态, 并释放当前对象的锁. 线程在调用 wait() 方法后, 进入"等待"状态, 直到其他线程调用同一对象上的 notify() 或 notifyAll() 方法来唤醒它.
- notify(): 唤醒在当前对象上等待的一个线程. 这个线程将会重新竞争锁, 等待重新获得锁之后才能继续执行.
- notifyAll(): 唤醒在当前对象上等待的所有线程. 所有等待的线程都会重新竞争锁, 并继续执行.
而condition提供的则是与wait/notify相对应的await()和signal(), 这几个方法都由Condition实例提供.
- await(): 使当前线程等待, 并释放锁, 直到其他线程调用相同条件上的 signal() 或 signalAll() 方法来唤醒它.
- awaitUninterruptibly(): 与 await() 类似, 但不响应中断.
- signal(): 唤醒一个在该条件上等待的线程. 如果有多个线程在等待, 只会唤醒其中一个, 具体唤醒哪个线程不确定.
- signalAll(): 唤醒所有在该条件上等待的线程.
当然这么介绍api对于没接触过等待通知机制的读者来说会完全不知所谓, 找不到重点. 因为这些知识点杂乱无章, 不成体系. 所以我们需要一个认知模型/体系将它们串起来.
那么, 问题回到了关键的部分: 什么是等待通知机制? 怎么使用等待通知机制? 在整个等待通知的流程中哪里用得上这些api?
设想一个单人经营的咖啡店中, 一个顾客过来购买咖啡的场景. 这里老板和顾客分别相当于线程A和线程B, 那么顾客从下单到买到咖啡总计有以下几个步骤.
- 顾客(线程B)进到咖啡店里, 扫描二维码获取咖啡菜单. 这一步等同于线程B获取锁开始执行某些操作.
- 顾客(线程B)选择自己想要的咖啡, 下单并付款, 然后找了个小桌子坐下开始玩手机. 这一步相当于线程B执行完前置操作, 调用wait/await, 进入等待状态.
- 老板(线程A)从后台接到新的已经付款的订单, 开始制备咖啡, 比如研磨, 加冰, 加牛奶等. 这一步相当于线程A获取到锁, 并在持有锁的期间执行A应该做的任务.
- 老板(线程A)准备好咖啡并放在服务台上, 通知客户可以来取咖啡了. 然后发现后台没有新订单, 转身去玩手机了. 这一步相当于线程A执行完毕, 调用 notify/signal 唤醒线程B, 完成这一步后释放锁.
- 顾客(线程B)收到提醒, 去服务台取走咖啡, 然后带着咖啡离开小店. 这一步相当于线程B被唤醒后, 尝试重新获取锁, 成功后执行后续的操作并最终释放锁.
当然, 现实世界中没有锁的对照物, 那是因为现实世界中每个人都有各自的脑袋, 所有人都是同时行动的. 而程序里是利用一个cpu模拟多个人的行为, 通常会有先后顺序, 为了防止cpu执行的两个线程互相修改了对方还没用完的参数, 需要加上锁以防万一.
1|3示例代码
好, 现在考虑在代码中实现上述的获取咖啡的操作
就像代码显示的那样, 在简单的等待通知机制中用不到notifyAll(), 这个api是为了处理可能存在多个顾客的这类的场景而准备的.
另外, 除了 synchronized/wait/notify 之外还有 Lock/Condition 这一组api也可以用来实现等待通知机制, 而且 Condition 的推出时间比较晚, 功能上更灵活.
上述两段代码分别用 synchronized/wait/notify 和 Lock/Condition 两组api实现了几乎相同的购买咖啡流程. 唯一的区别在于 Lock/Condition 中 lock 可以对应多个 condition. 所以同一个lock可以针对不同的线程甚至是同一个线程中不同的步骤使用不同的condition进行等待和唤醒来分别处理. 这一功能拓展可以很方便地解决更多的拓展场景.
1|4真实案例
等待通知机制特别是其高级版本生产者-消费者模式在编程中应用非常广泛, 但是很多时候都用在了被封装好的底层库和中间件上. 一般的新手工程师想找到一个用得着的场景不太容易.
而前面的例子只是解决了「等待通知机制是什么」「怎么使用等待通知机制」, 但是没有解决「在哪些场景可以用得上等待通知机制」这个问题.
所以这里给出一个实际场景, 希望能提供一些启发. 同时, 因为实际场景的代码量比较高, 只能选择伪代码 + 文字阐述的方式来描述.
由于模块的设计, 读取功能是在IO线程中由StartListening中的循环持续运行而实现的, 也就是说, 读取和写入部分被拆分开来, 适合与多个连接对象实现单次通信, 模块项目发送一条信息, 对方返回一条信息. 或者反过来对方返回一条信息, 模块发送一条信息.
但是现在出现了额外的需求, 需要将一个十几M的文件发送给连接对象. 由于自定义规则限制, 单个数据包大小不超过1024kb, 去掉包头包尾和各种验证时间戳, 有效的data数据部分会更短.
这时候就可以选择使用等待通知机制. 这里使用 Lock/Condition 这一组更灵活的api来实现
1|5总结
在本文中, 我们探讨了多线程编程中等待通知机制的适用场景及其不同实现方式. 通过比较 synchronized/wait/notify 和 Lock/Condition 以及实际案例可以看到后者在灵活性和可扩展性方面具有的优势. 实际场景中的文件传输示例展示了如何利用这些机制处理复杂的通信需求, 提高了程序的可靠性和效率.
__EOF__

本文链接:https://www.cnblogs.com/dwcg/articles/18323388.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix