Java EE初阶——wait 和 notify

 1. 线程饥饿

线程饥饿是指一个或多个线程因长期无法获取所需资源(如锁,CPU时间等)而持续处于等待状态,导致其任务无法推进的现象。

典型场景

  1. 优先级抢占

    • 在支持线程优先级的系统中,高优先级线程可能持续抢占CPU资源

    • 导致低优先级线程长期无法获得CPU时间片

  2. 不公平锁竞争

    • 某些线程频繁获取锁,其他线程长期等待

    • 典型场景:某个线程释放锁后立即重新竞争并获得锁

    • 导致其他线程始终处于BLOCKED状态而无法执行

  3. 资源分配不均

    • 某些线程占用大量I/O或内存资源

    • 其他线程因资源不足而无法执行

  4. 线程池配置不当

    • 固定大小线程池中,长任务占用所有线程

    • 短任务无法得到执行机会

关键问题点

  1. 锁获取模式问题

    • 活跃线程释放锁后立即重新请求锁

    • 处于RUNNABLE状态的线程比BLOCKED状态的线程有更快的响应速度

    • 操作系统唤醒BLOCKED线程需要上下文切换,造成竞争劣势

  2. 系统调度机制

    • 默认调度策略可能不利于公平性

    • 缺乏有效的防饥饿机制

  3. 线程状态转换开销

    • BLOCKED→RUNNABLE状态转换需要系统介入

    • 这种转换比保持RUNNABLE状态有更高的延迟

2. wait 和 notify

wait/notify 的本质作用

  • 应用层协作工具wait() 和 notify() 是 Java 提供的应用层线程协作机制,用于控制线程对共享资源的访问顺序,而非直接干预操作系统的线程调度策略。
  • 不改变调度规则:操作系统内核仍按自身调度算法(如轮转法、优先级调度)决定线程何时获得 CPU 时间,wait/notify 无法强制指定某个线程优先执行。

1. wait()⽅法

wait(); 内部做的三件事:

1. 立即释放锁,无需等待同步块结束

2. 线程状态变化RUNNING → WAITING

3. 线程被唤醒后需重新获取锁,获取成功后从 wait() 调用处继续执行。WAITING→ BLOCKED(被唤醒后重新竞争锁)→ RUNNING

线程状态变化后,其他线程就有机会获取锁。

wait() 方法的三种重载形式

方法说明
wait()使当前线程无限期等待,直到另一个线程调用 notify() 或 notifyAll() 方法
wait(long timeout)指定一个超时时间,线程将在超时后自动被唤醒。线程也可以在超时前被 notify() 或 notifyAll() 方法唤醒。
wait(long timeout, int nanos)提供更高精度的超时设置,总超时时间(以纳秒为单位)计算为 1_000_000*timeout + nanos

在 Java 中,调用 wait 方法的对象必须和锁对象一致,这是因为 wait 方法的行为是基于对象的监视器(锁)来实现的。以下是具体解释:

  • 原理:当一个线程调用某个对象的 wait 方法时,该线程会释放它所持有的该对象的锁,并进入等待状态,直到其他线程调用同一个对象的 notify 或 notifyAll 方法来唤醒它。如果调用 wait 方法的对象与获取锁的对象不一致,那么线程在等待时就无法正确地与该锁关联,也就无法按照预期被唤醒,并且可能会导致程序出现逻辑错误。

2. notify()⽅法

方法说明
notify()唤醒等待该对象监视器的一个随机线程。选择唤醒哪个线程是非确定性的,取决于“随机调度”算法
notifyAll()唤醒所有等待该对象监视器的线程。被唤醒的线程会和其他试图获取该对象锁的线程一起竞争锁

调用 notify() 或 notifyAll() 的对象必须与调用 wait() 的对象相同,并且它们必须与 synchronized 使用的锁对象一致,否则会抛出 IllegalMonitorStateException

  1. wait()notify()notifyAll() 必须由同一个对象调用

  2. 必须在 synchronized 块中使用,并且锁对象必须与调用 wait()/notify() 的对象一致

每个 Java 对象都有一个监视器(monitor),也可以理解为锁。当一个线程进入synchronized代码块时,它会获取该代码块所关联对象的锁。wait方法会让当前线程释放这个锁,并进入等待状态,直到其他线程调用同一个对象的notifynotifyAll方法来唤醒它。而notifynotifyAll方法也需要在获取相同对象的锁之后才能调用,这样它们才能准确地唤醒在该对象上等待的线程。如果这三个对象不一致,就会破坏这种线程同步机制,导致程序出现不可预测的结果,例如线程无法被唤醒、死锁等问题。

wait()notify() 和 notifyAll() 方法必须在 synchronized 修饰的代码块或方法中调用,否则会抛出 IllegalMonitorStateException

1. 锁与等待队列的绑定

每个 Java 对象都有两个核心属性:

  • 监视器锁(Monitor):用于实现同步。
  • 等待队列(Wait Set):用于存储调用 wait() 的线程。

wait() 和 notify() 的操作对象是对象的等待队列,而等待队列的状态由来保护。因此,必须先获取锁才能操作等待队列。

2. 原子性与可见性保障
public class SynchronizedDomo8 {public static void main(String[] args) throws InterruptedException {Object object = new Object();// 作为同步锁和 wait/notify 的监视器对象Thread t1 = new Thread(()->{synchronized (object){// 获取 object 的锁System.out.println("t1 线程之前");// ①try {//必须使用同一个对象调用object.wait();// ② 释放锁,进入WAITING状态} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("t1 线程之后");//  ⑦  被唤醒后重新获取锁,继续执行}});Thread t2 = new Thread(()->{try {Thread.sleep(2000);// ③ 休眠 2 秒,确保 t1 先执行并进入 wait()} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (object){// 获取 object 的锁System.out.println("t2 线程之前");// ④//必须使用同一个对象调用object.notify();  // ⑤ 唤醒t1,但t2仍持有锁System.out.println("t2 线程之后");// ⑥ 同步块结束后释放锁}});t1.start();t2.start();}
}

执行步骤

  1. t1 进入 synchronized 块,获取 object 的锁。

  2. 打印 "t1 线程之前"

  3. 调用 object.wait()

    • 释放 object 的锁t1 进入等待状态。

  4. t2 先休眠 2 秒,确保 t1 先执行并进入 wait() 状态。

  5. t2 进入 synchronized 块,获取 object 的锁。

  6. 打印 "t2 线程之前"

  7. 调用 object.notify()

    • 唤醒 t1(WAITING -> BLOCKED),但 t1 不会立即执行,因为 t2 仍持有锁。

  8. 打印 "t2 线程之后",退出 synchronized 块,释放锁。

  9. t1 重新获取锁,继续执行 "t1 线程之后"

3. wait()、join()、sleep()方法的区别

方法sleep()join()wait()
所属类Thread类Thread类 Object类 
释放锁
唤醒条件 时间到期目标线程结束或超时notify()/notifyAll()或超时
使用限制 可以直接调用可以直接调用必须在同步块中使用 
 抛出InterruptedException
精度控制毫秒(实际精度依赖操作系统) 毫秒+纳秒  毫秒+纳秒 
线程状态变化RUNNING → WAITINGRUNNING → TIMED_WAITINGRUNNING → WAITING/TIMED_WAITING

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/80323.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MATLAB中heatmap函数

无论对表格还是对矩阵的可视化,都非常好用。 样本特征 高斯核 https://ww2.mathworks.cn/help/matlab/creating_plots/create-heatmap-from-tabular-data.html

win11安装Joplin Server私有化部署(docker)

摘要 本指南将帮助你在 Windows 11 系统 上通过 Docker Docker Compose 完成 Joplin Server 的本地搭建,并实现数据持久化、PostgreSQL 后端支持、用户登录与同步功能。 条件说明✅ 已安装 Docker Desktop for Windows可从 Docker 官网 下载并安装,建议…

嵌入式STM32学习——外部中断EXTI与NVIC的基础练习⭐

按键控制LED灯 按键控制LED的开发流程: 第一步:使能功能复用时钟 第二布,配置复用寄存器 第三步,配置中断屏蔽寄存器 固件库按键控制LED灯 外部中断EXTI结构体:typedef struct{uint32_t EXTI_Line; …

《Deepseek从入门到精通》清华大学中文pdf完整版

资源介绍: 《DeepSeek:从入门到精通》是由清华大学新闻与传播学院新媒体研究中心元宇宙文化实验室的精心撰写的一份专业文档。该文档以通俗易懂的方 式,全面介绍了DeepSeek的使用方法,为用户提供了极具价值的指导。 这份文档内容丰…

Apache Pulsar 消息、流、存储的融合

Apache Pulsar 消息、流、存储的融合 消息队列在大层面有两种不同类型的应用,一种是在线系统的message queue,一种是流计算,data pipeline的streaming高throughout,一致性较低,延迟较差的过程。 存算分离 扩容和缩容快…

JavaScript vs Python 用于 Web Scraping(2025):终极对比指南

1. 引言 在不断发展的 Web Scraping 领域,选择合适的编程语言对于项目的成功至关重要。虽然 JavaScript 和 Python 在 2025 年仍然是 Web Scraping 领域的热门选择,但它们各自具备不同的优势和挑战。 本指南将深入分析 JavaScript 和 Python 的核心特性…

【RocketMQ Broker 相关源码】- NettyRemotingClient 和 NettyRemotingServer

文章目录 1. 前言2. BrokerOuterAPI2.1 NettyRemotingClient2.2 start 启动2.2.1 NettyRemotingClient#start 3. NettyRemotingServer3.1 ClientHousekeepingService3.2 ProducerManager#doChannelCloseEvent3.3 ConsumerManager#doChannelCloseEvent3.3.1 DefaultConsumerIdsC…

C++性能测试工具——AMD CodeAnalyst及其新工具的使用

一、CodeAnalyst及其新的替代工具 与VTune相比,AMD也有自己的性能测试工具,也就是CodeAnalyst。不过目前看,其应该已经有些过时,目前AMD提供了更新的性能测试工具uProf或CodeXL,这些新工具的优点在于对新的硬件架构和…

ProfibusDP主站转modbusTCP网关与ABB电机保护器数据交互

ProfibusDP主站转modbusTCP网关与ABB电机保护器数据交互 在工业自动化领域,Profibus DP(Process Field Bus)和Modbus TCP是两种常见的通讯协议,它们各自在不同的场合发挥着重要作用。然而,随着技术的发展和应用需求的…

2025.05.17淘天机考笔试真题第三题

📌 点击直达笔试专栏 👉《大厂笔试突围》 💻 春秋招笔试突围在线OJ 👉 笔试突围OJ 03. 奇偶平衡树分割问题 问题描述 K小姐是一位园林设计师,她设计了一个由多个花坛组成的树形公园。每个花坛中种植了不同数量的花…

第三十五节:特征检测与描述-ORB 特征

1. 引言:为什么需要ORB? 在计算机视觉领域,特征检测与描述是许多任务(如图像匹配、目标跟踪、三维重建等)的核心基础。传统的算法如SIFT(尺度不变特征变换)和SURF(加速稳健特征)因其优异的性能被广泛应用,但它们存在两个显著问题: 专利限制:SIFT和SURF受专利保护,…

深入解读WPDRRC信息安全模型:构建中国特色的信息安全防护体系

目录 前言1 WPDRRC模型概述2 模型结构详解2.1 预警(Warning)2.2 保护(Protect)2.3 检测(Detect)2.4 响应(React)2.5 恢复(Restore)2.6 反击(Count…

《算法导论(第4版)》阅读笔记:p82-p82

《算法导论(第4版)》学习第 17 天,p82-p82 总结,总计 1 页。 一、技术总结 1. Matrix Matrices(矩阵) (1)教材 因为第 4 章涉及到矩阵,矩阵属于线性代数(linear algebra)范畴,如果不熟悉,可以看一下作者推荐的两本…

基于Spring Boot和Vue的在线考试系统架构设计与实现(源码+论文+部署讲解等)

源码项目获取联系 请文末卡片dd我获取更详细的演示视频 系统介绍 基于Spring Boot和Vue的在线考试系统。为学生和教师/管理员提供一个高效、便捷的在线学习、考试及管理平台。系统采用前后端分离的架构,后端基于成熟稳定的Spring Boot框架,负责数据处理…

Codeforces Round 1024 (Div.2)

比赛链接&#xff1a;CF1024 A. Dinner Time 只有当 n n n 是 p p p 的倍数而且 n ⋅ q p ̸ m \frac{n \cdot q}{p} \not m pn⋅q​m 时输出 NO&#xff0c;其余情况均满足条件。 时间复杂度&#xff1a; O ( 1 ) O(1) O(1)。 #include <bits/stdc.h> using na…

【LeetCode 热题 100】二叉树的最大深度 / 翻转二叉树 / 二叉树的直径 / 验证二叉搜索树

⭐️个人主页&#xff1a;小羊 ⭐️所属专栏&#xff1a;LeetCode 热题 100 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 二叉树的中序遍历二叉树的最大深度翻转二叉树对称二叉树二叉树的直径二叉树的层序遍历将有序数组转换为二叉搜索树验…

Tomcat发布websocket

一、tomcal的lib放入文件 tomcat-websocket.jar websocket-api.jar 二、代码示例 package com.test.ws;import com.test.core.json.Jmode;import javax.websocket.*; import javax.websocket.server.ServerEndpoint; import java.util.concurrent.CopyOnWriteArraySet; imp…

LLM笔记(二)LLM数据基础-分词算法(2)

文章目录 1. 分词算法概述1.1 基于词典的&#xff08;或基于规则的&#xff09;分词算法1.2 基于统计的&#xff08;或基于机器学习的&#xff09;分词算法1.3 基于深度学习的分词算法1.4 子词&#xff08;Subword&#xff09;分词算法1.5 混合分词算法1.6 针对不同语言的特点 …

Uniapp开发鸿蒙应用时如何运行和调试项目

经过前几天的分享&#xff0c;大家应该应该对uniapp开发鸿蒙应用的开发语法有了一定的了解&#xff0c;可以进行一些简单的应用开发&#xff0c;今天分享一下在使用uniapp开发鸿蒙应用时怎么运行到鸿蒙设备&#xff0c;并且在开发中怎么调试程序。 运行 Uniapp项目支持运行到…

数据湖与数据仓库融合:Hudi、Iceberg、Delta Lake 实践对比

在实时与离线一体化的今天,数据湖与数据仓库边界不断融合,越来越多企业选用如 Hudi、Iceberg、Delta Lake 等开源方案实现统一的数据存储、计算、分析平台。本篇将围绕以下关键点,展开实战对比与解决方案分享: ✅ 实时写入能力 ✅ ACID 保证 ✅ 增量数据处理能力 ✅ 流批一…