当前位置: 首页 > article >正文

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

相关文章:

Java EE初阶——wait 和 notify

1. 线程饥饿 线程饥饿是指一个或多个线程因长期无法获取所需资源(如锁,CPU时间等)而持续处于等待状态,导致其任务无法推进的现象。 典型场景 优先级抢占: 在支持线程优先级的系统中,高优先级线程可能持续…...

RPA vs. 传统浏览器自动化:效率与灵活性的终极较量

1. 引言 在数字化转型的大潮下,企业和开发者对浏览器自动化的需求日益增长。无论是网页数据抓取、自动化测试,还是用户行为模拟,浏览器自动化已经成为提升效率的关键工具。然而,面对越来越严格的反自动化检测、复杂的 Web 结构和…...

Flask框架深度解析:蓝图、上下文机制与Jinja2模板引擎实战

Flask作为Python最流行的轻量级Web框架之一,以其简洁、灵活和高度可扩展的特性赢得了广大开发者的青睐。本文将深入探讨Flask框架的三大核心特性:蓝图(Blueprint)模块化开发、上下文(Context)管理机制以及Jinja2模板引擎的高级用法。无论你是Flask初学者…...

docker 快速部署若依项目

1、首先创建一个自定义网络,作用是使连接到该网络的容器能够通过容器名称进行通信,无需使用复杂的IP地址配置,方便了容器化应用中各个服务之间的交互。 sudo docker network create ruoyi 2、创建一个文件夹,创建compose.yml文件…...

polarctf-web-[rce1]

考点: (1)RCE(exec函数) (2)空格绕过 (3)执行函数(exec函数) (4)闭合(ping命令闭合) 题目来源:Polarctf-web-[rce1] 解题: 这段代码实现了一个简单的 Ping 测试工具,用户可以通过表单提交一个 IP 地址,服务器会执…...

数据备份与恢复方案

数据备份与恢复方案 一.背景 为确保公司信息安全,防止关键数据丢失,应对突发事件,特制定全面的数据备份与恢复方案。该方案将对公司的各类文件资料进行分级管理,并针对不同级别的数据设定相应的备份策略和恢复流程。 二…...

Redis+Caffeine构造多级缓存

一、背景 项目中对性能要求极高,因此使用多级缓存,最终方案决定是RedisCaffeine。其中Redis作为二级缓存,Caffeine作为一级本地缓存。 二、Caffeine简单介绍 Caffeine是一款基于Java 8的高性能、灵活的本地缓存库。它提供了近乎最佳的命中…...

docker(四)使用篇二:docker 镜像

在上一章中,我们介绍了 docker 镜像仓库,本文就来介绍 docker 镜像。 一、什么是镜像 docker 镜像本质上是一个 read-only 只读文件, 这个文件包含了文件系统、源码、库文件、依赖、工具等一些运行 application 所必须的文件。 我们可以把…...

ms-swift 代码推理数据集

目前想要对SFT微调后的模型进行测试,看官方文档ms-swift中有eval的教程,但是从介绍来看,eval使用的是modelscope的评测内容。 评测 SWIFT支持了eval(评测)能力,用于对原始模型和训练后的模型给出标准化…...

AXI4总线协议 ------ AXI_LITE协议

一、AXI 相关知识介绍 https://download.csdn.net/download/mvpkuku/90841873 AXI_LITE 选出部分重点,详细文档见上面链接。 1.AXI4 协议类型 2.握手机制 二、AXI_LITE 协议的实现 1. AXI_LITE 通道及各通道端口功能介绍 2.实现思路及框架 2.1 总体框架 2.2 …...

DATE_FORMAT可以接收date类型,也可以接收String类型!

DATE_FORMAT 是 SQL 函数,主要用于将日期/时间类型的字段按照指定格式转换成字符串。在 MyBatis 的 XML 动态 SQL 中,你看到的这段代码是为了比较数据库中的日期字段和传入参数的日期值,但会忽略时间部分,只比较年月日。 代码解释…...

Ubuntu24.04 安装 5080显卡驱动以及cuda

前言 之前使用Ubuntu22.04版本一直报错,然后换了24.04版本才能正常安装 一. 配置基础环境 Linux系统进行环境开发环境配置-CSDN博客 二. 安装显卡驱动 1.安装驱动 按以下步骤来: sudo apt update && sudo apt upgrade -y#下载最新内核并安装 sudo add…...

华三H3C交换机配置NTP时钟步骤 示例

现场1台H3C 5110交换机 版本&#xff1a;Comware Software, Version 5.20.99, Release 1105 当前没有指定NTP&#xff0c; <H3C-5110>dis ntp-service status Clock status: unsynchronizedClock stratum: 16Reference clock ID: noneNominal frequency: 100.0000 HzAc…...

RKNN开发环境搭建(ubuntu22.04)

以下情况在RV1106G3的平台上验证正常。 1、conda安装 1&#xff09;conda --version//确认是否安装 2&#xff09;创建一个安装目录&#xff0c;进行下一步 3&#xff09;wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-4.6.14-Linux-x…...

matlab多项式

1. 多项式表示 多项式用行向量表示&#xff0c;按降幂排列系数。例如&#xff0c;多项式 3x22x1 表示为 [3 2 1]。 2. 创建多项式 直接输入系数&#xff1a;如 p [1 -3 3 -1] 表示 x3−3x23x−1。由根创建&#xff1a;使用 poly 函数。例如&#xff0c;根为 [1, 1, 1]&…...

Sprnig MVC 如何统一异常处理 (Exception Handling)?

主要有以下几种方式来实现统一异常处理&#xff0c;其中 ControllerAdvice (或 RestControllerAdvice) 结合 ExceptionHandler 是最常用的方式。 1. ExceptionHandler 注解 作用&#xff1a; 用于标记一个方法&#xff0c;该方法将处理在同一个 Controller 类中抛出的特定类型…...

SpringAI-RC1正式发布:移除千帆大模型!

续 Spring AI M8 版本之后&#xff08;5.1 发布&#xff09;&#xff0c;前几日 Spring AI 悄悄的发布了最新版 Spring AI 1.0.0 RC1&#xff08;5.13 发布&#xff09;&#xff0c;此版本也将是 GA&#xff08;Generally Available&#xff0c;正式版&#xff09;发布前的最后…...

操作系统之进程和线程听课笔记

计算机的上电运行就是构建进程树,进程调度就是在进程树节点进程进行切换 进程间通信的好处 经典模型 生产者和消费者 进程和线程的区别 线程引入带来的问题线程的优势 由于unix70年代产生,90年代有线程,当时数据库系统操作需要线程,操作系统没有来得及重造,出现了用户态线…...

【vue】封装接口,全局字典,表格表头及使用

一、封装接口&#xff08;API请求&#xff09; 1. 创建axios实例 // src/utils/request.js import axios from axiosconst service axios.create({baseURL: process.env.VUE_APP_BASE_API,timeout: 10000 })// 请求拦截器 service.interceptors.request.use(config > {co…...

深入解析ZAB协议:ZooKeeper的分布式一致性核心

引言 在分布式系统中&#xff0c;如何高效、可靠地实现多节点间的数据一致性是核心挑战之一。ZAB协议&#xff08;ZooKeeper Atomic Broadcast&#xff09;作为 ZooKeeper的核心算法&#xff0c;被广泛应用于分布式协调服务&#xff08;如Kafka、HBase、Dubbo等&#xff09;。…...

COMSOL随机参数化表面流体流动模拟

基于粗糙度表面的裂隙流研究对于理解地下水的流动、污染物传输以及与之相关的地质灾害&#xff08;如滑坡&#xff09;等方面具有重要意义。本研究通过蒙特卡洛方法生成随机表面形貌&#xff0c;并利用COMSOL Multiphysics对随机参数化表面的微尺度流体流动进行模拟。 参数化…...

大模型笔记-“训练”和“推理”概念

在大模型&#xff08;如Transformer类模型、LLM&#xff09;的资源管理和开发流程中&#xff0c;“训练”和“推理”是两个核心概念&#xff0c;分别对应模型的构建和实际应用阶段&#xff1a; 训练是模型的“学习过程”&#xff0c;需要大量资源和时间。推理是模型的“应用过…...

JavaSwing中的容器之--JScrollPane

JavaSwing中的容器之–JScrollPane 在Java Swing中&#xff0c;容器是用于容纳其他组件&#xff08;如按钮、标签等&#xff09;的组件。Swing提供了多种容器&#xff0c;它们可以嵌套使用以创建复杂的用户界面。 JScrollPane是一个轻量级组件&#xff0c;提供可滚动视图。JSc…...

使用 Cookie 实现认证跳转功能

使用 Cookie 实现认证跳转功能的实践与解析 在 Web 开发中&#xff0c;用户身份认证是一个基础而关键的功能点。本文将通过一个简单的前后端示例系统&#xff0c;介绍如何基于 Cookie 实现 Token 保存与自动跳转认证的功能&#xff0c;并结合 Cookie 与 Header 的区别、使用场…...

Reth(冗余以太网接口) 和Bridge-Aggregation(链路聚合接口)区别

Reth&#xff08;Redundant Ethernet&#xff09;与Bridge-Aggregation是H3C设备中两种不同的接口技术&#xff0c;主要区别体现在工作原理、应用场景及配置特性上。以下是详细对比分析&#xff1a; 定义与类型 Reth&#xff08;冗余以太网接口&#xff09; 类型&#xff1a;…...

(面试)Android各版本新特性

Android 6.0 (Marshmallow, API 23) 运行时权限管理&#xff1a;用户可在应用运行时动态授予或拒绝权限&#xff0c;取代安装时统一授权4。Doze模式与应用待机&#xff1a;优化后台耗电&#xff0c;延长设备续航5。指纹识别支持&#xff1a;原生API支持指纹身份验证。 Android…...

算法基础 -- 小根堆构建的两种方式:上浮法与下沉法

小根堆构建的两种方式&#xff1a;上浮法与下沉法 在构建小根堆&#xff08;Min-Heap&#xff09;时&#xff0c;通常有两种常见的构建方式&#xff1a; 上浮建堆&#xff08;逐个插入&#xff0c;上浮调整&#xff09;下沉建堆&#xff08;Heapify 自底向上&#xff0c;下沉…...

LED接口设计

一个LED灯有3种控制状态&#xff0c;常亮、常灭和闪烁&#xff0c;要做到这种控制最简单的一种方法是使用任何一款处理器的普通IO去控制。 用IO控制方式有两种&#xff0c;一种是高有效&#xff0c;如下图1所示IO口为高电平时LED亮&#xff0c;IO为低电平时LED不亮。IO口出一个…...

西安前端面试

面试1 1.vue2和vue3的原理及区别 2.伪数组 3.对箭头函数怎么理解的 4.vue父子组件传值的几种方式 5.对Promise的理解 面试2 1.两个升序数组实现合并升序排序 2.数组拍平[3, [[7, [1, 5]], 4], 8, [6]] 面试3 1.let var const的区别&#xff0c;什么时候const能改变 …...

SpringBoot项目使用POI-TL动态生成Word文档

近期项目工作需要动态生成Word文档的需求&#xff0c;特意调研了动态生成Word的技术方案。主要有以下两种&#xff1a; 第一种是FreeMarker模板来进行填充&#xff1b;第二种是POI-TL技术使用Word模板来进行填充&#xff1b; 以下是关于POI-TL的官方介绍 重点关注&#xff1…...