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

Java多线程通信:wait/notify与sleep的深度剖析(时序图详解)

在Java多线程编程中,线程间的通信与协作是实现复杂并发逻辑的关键。wait()notify()以及sleep()方法作为线程控制的重要工具,有着各自独特的使用场景与规则。本文将深入探讨wait()notify()的协作机制,以及sleep()的阻塞特性,同时重点解析wait()必须在循环中调用的核心原因——防止虚假唤醒(Spurious Wakeup)。

一、wait/notify:线程间通信的桥梁

1.1 wait/notify的核心功能

wait()notify()是Object类的方法,主要用于线程间的协作与通信。

  • wait():当一个线程调用wait()方法时,它会释放当前持有的对象锁,并进入等待状态。直到其他线程调用同一个对象的notify()notifyAll()方法将其唤醒。
  • notify():随机唤醒一个在该对象上等待的线程;而notifyAll()则会唤醒所有在该对象上等待的线程。

关键操作时序图
线程通信时序图

1.2 wait/notify的使用规则

wait()notify()必须在synchronized同步代码块或同步方法中使用,这是因为它们的操作依赖于对象锁的状态。若不在同步环境中调用,会抛出IllegalMonitorStateException异常。

1.3 典型使用场景:生产者-消费者模型

class SharedResource {private int data;private boolean isEmpty = true;public synchronized void produce(int value) {while (!isEmpty) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}data = value;isEmpty = false;notifyAll();}public synchronized void consume() {while (isEmpty) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("Consumed: " + data);isEmpty = true;notifyAll();}
}

在上述代码中,生产者线程在缓冲区满时调用wait()等待,消费者线程消费数据后通过notifyAll()唤醒生产者;反之亦然。

二、sleep:简单的线程阻塞

2.1 sleep的特性

sleep()是Thread类的静态方法,用于让当前线程暂停执行指定的时间(以毫秒为单位)。与wait()不同,sleep()不会释放线程持有的锁。

2.2 使用场景

  • 模拟延迟:在测试代码或需要控制执行节奏的场景中,通过sleep()让线程暂停一段时间。
  • 避免过度占用资源:例如在轮询检查某个条件时,加入sleep()可以减少CPU的占用。
public class SleepExample {public static void main(String[] args) {Thread thread = new Thread(() -> {synchronized (SleepExample.class) {try {System.out.println("线程开始睡眠");Thread.sleep(2000);System.out.println("线程睡眠结束");} catch (InterruptedException e) {e.printStackTrace();}}});thread.start();}
}

在上述代码中,线程在持有锁的情况下进行睡眠,期间其他线程无法获取该锁。

三、为什么wait()必须在循环中调用:防止虚假唤醒

3.1 什么是虚假唤醒

虚假唤醒(Spurious Wakeup)是指线程在没有被其他线程调用notify()notifyAll()的情况下被唤醒。虽然这种情况在实际中并不常见,但Java的wait()机制允许它发生,这是JVM底层实现的结果。

3.2 循环检查的必要性

如果不使用循环,当发生虚假唤醒时,线程会误以为等待的条件已经满足,继续执行后续代码,可能导致程序逻辑错误。因此,wait()必须配合while循环使用,每次被唤醒后重新检查条件是否真正满足。

synchronized (obj) {while (!condition) {obj.wait();}// 条件满足,执行相应逻辑
}

通过while循环,即使发生虚假唤醒,线程也会重新进入等待状态,直到条件真正满足才继续执行。

四、总结

  • wait/notify:用于线程间的通信与协作,必须在同步环境中使用,且wait()要配合while循环防止虚假唤醒。
  • sleep:单纯的线程阻塞,不释放锁,适用于控制线程执行节奏。

理解这些方法的特性与使用规则,是编写正确、高效的多线程程序的基础。在实际开发中,合理运用这些工具,可以实现复杂的线程协作逻辑,同时避免因不当使用导致的各种问题。

http://www.xdnf.cn/news/14394.html

相关文章:

  • Spring是如何实现有代理对象的循环依赖
  • 【SQLAlchemy系列】 SQLAlchemy 中的多条件查询:or*与 in*操作符
  • 智能土木通 - 土木工程专业知识问答系统02-RAG检索模块搭建
  • AC耦合与DC耦合
  • 体验AI智能投资!AI Hedge Fund了解一下
  • Java可变参数方法的常见错误与最佳实践
  • hyper-v虚拟机使用双屏
  • iOS —— UI(2)
  • Spring Cloud 所有组件全面总结
  • 「AI大数据」| 智慧公路大数据运营中心解决方案
  • Java类加载器与双亲委派模型深度解析
  • DNS递归查询
  • BOLL指标
  • Oracle21cR3之客户端安装错误及处理方法
  • 第11章 结构 笔记
  • 华为OD-2024年E卷-小明周末爬山[200分] -- python
  • 亚马逊ASIN: B0DNTQ2YNT数据深度解析报告
  • 3.创建数据库
  • STM32103CBT6显示ST7789通过SPI方式显示柬埔寨文
  • Unity Addressable使用之入门篇
  • 讲一下进程和线程
  • Day54打卡 @浙大疏锦行
  • 37-Oracle 23 ai Shrink Tablespace(一键收缩表空间)
  • Composer 的 PHP 依赖库提交教程
  • 【Qt】Qt 基础
  • Redis-CPP通用接口
  • Leetcode 3584. Maximum Product of First and Last Elements of a Subsequence
  • 139. 单词拆分
  • (LeetCode 每日一题) 1432. 改变一个整数能得到的最大差值(贪心)
  • React组件通信——context(提供者/消费者)