当前位置: 首页
编程语言
怎么通过 Object 类的 wait/notify 机制在面向对象层面实现初级的线程间协作

怎么通过 Object 类的 wait/notify 机制在面向对象层面实现初级的线程间协作

热心网友 时间:2026-05-03
转载

怎么通过 Object 类的 wait/notify 机制在面向对象层面实现初级的线程间协作

怎么通过 Object 类的 wait/notify 机制在面向对象层面实现初级的线程间协作

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈

在Ja va多线程编程里,让线程之间有序协作,而不是乱成一锅粥,是个基本功。Object 类提供的 wait()notify()(以及它的兄弟 notifyAll())方法,正是实现这种“等待-唤醒”契约的核心工具。简单来说,就是让一个线程在条件不满足时先“歇着”,等另一个线程准备好后再把它“叫醒”。

但这里有个铁律,必须牢记:所有对 waitnotify 的调用,都必须老老实实地放在 synchronized 同步块里,而且锁对象,必须就是调用这些方法的那个对象本身。这是整个机制能安全运转的基石。

共享状态对象作为协作枢纽

怎么理解这个“契约”呢?关键在于,要有一个共享的状态对象。你可以定义一个普通的Ja va类,比如叫 TaskQueue,用它来封装需要协作的业务状态——比如任务列表是不是空了,或者计算结果有没有准备好。这个对象身兼三职:它是数据的载体,是同步用的锁,更是线程间通信的“信号灯”。

  • 线程A想调用 queue.wait() 去等待,前提是它必须先拿到 queue 这把锁(也就是进入 synchronized(queue) 块)。一旦调用 wait(),它就会释放锁,然后乖乖进入等待队列,直到被唤醒。
  • 线程B在修改了共享状态(比如往队列里加了新任务)之后,同样需要在 synchronized(queue) 块里,调用 queue.notify()queue.notifyAll() 来发出信号。
  • 被唤醒的线程(比如刚才的线程A)会重新加入竞争,去抢 queue 的锁。抢到之后,它才能从 wait() 的地方继续执行。这里有个至关重要的细节:醒来后第一件事,应该是重新检查等待条件是否真的满足了。

必须配合 while 循环做条件重检

为什么必须重新检查?因为 wait() 存在“虚假唤醒”的可能性——线程可能因为某些与业务逻辑无关的原因(比如操作系统信号干扰)而被唤醒。所以,绝对不能简单地用 if 判断一次就了事,必须用 while 循环把等待条件包起来:

synchronized (sharedObj) {
  while (!conditionMet()) { // 用while,不是if!
    sharedObj.wait();
  }
  // 执行到这里时,conditionMet() 肯定为 true了,可以安全地处理后续逻辑
}

举个典型的例子,在生产者-消费者模型里,消费者线程能行动的前提是“队列非空”。所以,它的等待逻辑必须是 while(!queue.isEmpty()) wait(); —— 即使被唤醒了,也得再瞅一眼队列,确认真的有东西可消费才行。

notify 与 notifyAll 的选择依据

那么,叫醒一个线程,是用 notify() 还是 notifyAll() 呢?这得看场景。

  • notify() 会随机唤醒等待在同一个对象上的一个线程。它适合条件单一、等待方角色明确的场景,比如两个线程严格交替执行。
  • notifyAll() 则更“豪爽”,它会唤醒所有正在等待的线程。这种方式更安全、更通用,尤其是在多个线程等待不同条件(比如有的在等“非空”,有的在等“未满”)的复杂场景下。唤醒后,让每个线程自己用 while 循环去判断是否满足了自己的条件,满足的就继续执行,不满足的继续等待。

对于初学者来说,有个实用的建议:如果不确定该用哪个,那就统一使用 notifyAll()。虽然可能带来一点点性能开销,但它能有效避免因唤醒错对象而导致的死锁风险。当然,如果你能百分之百确定,只有一类线程在等待某个条件,并且你一旦唤醒它,条件肯定满足,那么用 notify() 会更轻量一些。

典型协作模式示例:简单阻塞队列

理论说了这么多,来看一个最经典的“生产一个,消费一个”的协作模式,思路就非常清晰了:

  • 生产者线程的行动路线是:获取锁 → 检查队列是否已满(用while循环判断)→ 如果满了就 wait() → 添加元素 → 调用 notifyAll() → 释放锁。
  • 消费者线程的行动路线是:获取锁 → 检查队列是否为空(同样用while循环判断)→ 如果空了就 wait() → 取出元素 → 调用 notifyAll() → 释放锁。

这个模式里的关键点有两个:一是双方都在修改完共享状态后、离开同步块前调用 notifyAll();二是双方都坚持用 while 循环来重新检查等待条件。把握住这两点,一个基础的线程协作框架就搭建起来了。

来源:https://www.php.cn/faq/2411171.html

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
VSCode设置编辑器边距_调整代码与侧边栏的间距

VSCode设置编辑器边距_调整代码与侧边栏的间距

VSCode 中真正控制编辑器内容区左右留白的是 editor padding 配置项 先明确一个核心概念:VSCode 里并没有一个叫“编辑器与侧边栏间距”的配置。我们常说的那种想让代码离边缘远一点的“边距”,其实指的是编辑器内容区自身的左右内边距。这个功能,完全由 editor padding

时间:2026-05-03 09:52
如何在VSCode中快速跳转到函数定义、声明或引用位置

如何在VSCode中快速跳转到函数定义、声明或引用位置

如何在VSCode中快速跳转到函数定义、声明或引用位置 为什么 Ctrl+Click 有时跳转失败? 按住 Ctrl(Windows Linux)或 Cmd(macOS)点击函数名就能跳转,这几乎是现代IDE的标配操作。但如果你试过,就会发现它偶尔会“失灵”。问题出在哪?其实,这个看似简单的功能,背

时间:2026-05-03 09:52
如何清理VSCode中C/C++ Intellisense产生的庞大ipch缓存文件夹释放C盘

如何清理VSCode中C/C++ Intellisense产生的庞大ipch缓存文件夹释放C盘

如何清理VSCode中C C++ Intellisense产生的庞大ipch缓存文件夹释放C盘 VSCode C C++插件的ipch目录到底占多少空间? 先说一个关键事实:这个ipch文件夹其实并不归VSCode本体管理。它是由微软官方的vscode-cpptools扩展生成的,具体来说,是插件在

时间:2026-05-03 09:52
Composer提示无法识别的仓库类型_检查repositories配置语法【配置纠错】

Composer提示无法识别的仓库类型_检查repositories配置语法【配置纠错】

“Unrecognized repository type” 错误深度解析与排查指南 遇到 Composer 报出“无法识别的仓库类型”这个错误,很多开发者第一反应是拼写问题。没错,但事情远不止于此。这个错误的本质是,Composer 在 repositories 配置中遇到了一个它完全不认识的 t

时间:2026-05-03 09:52
如何修改VSCode编辑器右侧的代码缩略图(Minimap)宽度

如何修改VSCode编辑器右侧的代码缩略图(Minimap)宽度

如何修改VSCode编辑器右侧的代码缩略图(Minimap)宽度 Minimap 宽度由哪个设置控制 如果你觉得VSCode右侧的代码缩略图(Minimap)太宽或太窄,想调整它,那么关键就在于一个设置项: "editor minimap width "。这个设置直接决定了Minimap的视觉宽度。 它

时间:2026-05-03 09:51
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 日榜
  • 周榜
  • 月榜
热门教程
更多
  • 游戏攻略
  • 安卓教程
  • 苹果教程
  • 电脑教程