java线程间的通信- notify和 wait

你好,我是 shengjk1,多年大厂经验,努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注!你会有如下收益:

  1. 了解大厂经验
  2. 拥有和大厂相匹配的技术等

希望看什么,评论或者私信告诉我!

文章目录

  • 一、前言
  • 二、notify 和 wait
    • 2.1 wait
      • 2.1.1 wait 基本介绍
      • 2.1.2 wait 注意点
      • 2.1.3 wait 使用场景
      • 2.1.4 wait 的执行原理
      • 2.1.5 wait 使用
    • 2.2 notify
      • 2.2.1 notify 基本介绍
      • 2.2.2 notify 注意点
      • 2.2.3 nofityall
      • 2.2.3 notify 使用场景
      • 2.2.4 notify 的执行原理
      • 2.2.5 notify和notifyAll 注意事项和要点:
  • 三、wait/nofity 经典使用方式
  • 四、总结


一、前言

在软件开发中,线程是实现并发执行的重要手段,然而,线程之间的协作与通信却是开发者必须重点考虑的挑战之一。Java作为一种广泛应用于多线程编程的语言,提供了一套强大而灵活的机制,让不同线程之间能够优雅地交替执行、传递信息,以实现协调合作。

前面我们已经完成了多线程一些基础知识准备以及 volatile 和 synchronized,本文将深入探讨Java中通过notifywait实现线程间通信的机制。

二、notify 和 wait

2.1 wait

2.1.1 wait 基本介绍

通过源码我们可以知道 wait 是 object 对象方法,用于实现线程间的等待和通知机制。当一个线程调用wait()方法时,它会释放当前所持有的对象锁,并进入等待状态,直到被其他线程调用相同对象上的notify()notifyAll()方法唤醒。

public final void wait() throws InterruptedException {wait(0);
}

2.1.2 wait 注意点

  1. 要想使用wait方法,当前线程必须拥有对应 object 的 mointor,即必须要拥有对应对象的锁,否则会报java.lang.IllegalMonitorStateException
    比如:
Object o = new Object();
o.wait(); //java.lang.IllegalMonitorStateException
  1. 当调用wait()方法的线程被另一个线程中断时,wait()方法会抛出InterruptedException异常。
  2. 线程也可能会在没有收到通知、中断或超时的情况下被唤醒,即所谓的虚假唤醒。虽然这种情况在实践中很少发生,但应用程序必须通过测试应该导致线程被唤醒的条件来防范这种情况,如果条件不满足,则继续等待。换句话说,等待应该总是在循环中发生,如下所示:
   synchronized (obj) {while (<condition does not hold>)obj.wait(timeout);... // Perform action appropriate to condition}

2.1.3 wait 使用场景

wait()方法是在Java中用于线程间通信和同步的重要方法之一。它通常与synchronized关键字一起使用,用于在多线程中协调线程之间的操作。下面是wait()方法的一些常见使用场景:

  1. 协调多个线程的操作wait()方法通常与notify()notifyAll()方法一起使用,用于在多线程之间协调操作。一个线程可以调用wait()进入等待状态,等待其他线程调用相同对象的notify()notifyAll()方法来唤醒它。
  2. 等待条件满足:线程可以调用wait()方法等待某个条件的满足。例如,一个线程可能等待某个变量的值发生改变或者等待某个事件发生。
  3. 线程安全的队列实现wait()方法常用于实现线程安全的队列。当队列为空时,消费者线程调用wait()等待生产者线程向队列中添加元素;当队列已满时,生产者线程调用wait()等待消费者线程从队列中取走元素。
  4. 实现生产者-消费者模型wait()方法在生产者-消费者模型中起着重要作用。生产者向共享的缓冲区添加数据时,如果缓冲区已满,生产者线程调用wait()等待消费者取走数据;消费者从缓冲区取数据时,如果缓冲区为空,消费者线程调用wait()等待生产者添加数据。
  5. 线程间通信wait()方法是线程间通信的重要手段之一。线程可以通过等待并唤醒的机制来实现信息的传递和协调。

2.1.4 wait 的执行原理

  • 当线程调用wait()方法时,它会释放当前持有的对象锁,使得其他线程可以访问这个对象并执行同步代码块。
  • 调用wait()方法的线程会进入对象的等待队列,等待其他线程调用相同对象的notify()notifyAll()方法来唤醒它。
  • 当另一个线程调用相同对象notify()方法或notifyAll()方法时,等待队列中的线程会被唤醒,然后竞争对象的锁。
  • 唤醒的线程会尝试重新获取对象锁,然后继续执行。

2.1.5 wait 使用

  1. wait()或者wait(0): 调用该方法的线程进入WAITING状态,只有等待另外线程的通知或被中断才会返回,需要注意调用 wait() 方法后,会释放对象的锁
  2. wait(long) 会释放锁,超时等待一段时间,这里的参数时间是毫秒,也就是等待长达毫秒,如果没有通知就超时返回
  3. wait(long,int) 会释放锁,该方法与 wait(long) 类似,多了一个 nanos 参数,这个参数表示额外时间(以纳秒为单位,范围是 0-999999), 所以超时的时间还需要加上 nanos 纳秒。

2.2 notify

首先我们需要知道 nofity 和 notifyall 基本上是等价的,如没有特别标明,nofity 和 nofityall 是一样的

2.2.1 notify 基本介绍

通过源码我们可以知道 notify 是 object 对象方法。

notify()是线程间通信的一种机制,用于唤醒在当前对象上等待的一个线程。当一个线程调用notify()方法时,它会唤醒正在该对象上等待的单个线程(如果有多个线程在等待,系统无法确定哪个线程会被唤醒,因为选择是随机的)。这个被唤醒的线程将从等待状态变为可运行状态,但并不意味着立即获得对象的锁。

public final native void notify();

当一个线程调用notifyAll()时,它会唤醒在当前对象上等待的所有线程,使它们从等待状态转变为可运行状态。这样,所有等待中的线程都有机会争取获取对象锁,在某个线程获得锁后,它们会竞争执行。

2.2.2 notify 注意点

下面是关于notify()方法的一些重要点:

  1. 使用条件变量notify()方法通常与条件变量一起使用,用于线程间的协作。一个线程等待某个条件变为真,另一个线程在某种情况下会改变条件,并调用notify()来通知等待的线程。
  2. notifyAll()方法:与notify()不同的是,notifyAll()方法唤醒在当前对象上等待的所有线程,而不仅仅是一个线程。这样做可以避免遗漏任何等待中的线程,但同一时间获取锁的只会有一个线程。
  3. Object类中的方法notify()方法是Object类的一个方法,因此任何Java对象都可以调用notify()wait()方法来进行线程间的通信。
  4. 必须在同步块中调用:为了调用notify()方法,必须在同步块(synchronized块)中对包含该对象的锁进行操作。
  5. 随机性:当多个线程在同一个对象上等待时,调用notify()会随机选择一个线程唤醒,因此不能确定哪个线程会被唤醒。
  6. 唤醒等待线程:被唤醒的线程会尝试重新获取对象锁,一旦获得锁,它会从wait()方法返回,并继续执行后续代码。

2.2.3 nofityall

以下是关于notifyAll()方法的详细介绍:

  1. 唤醒所有等待线程notifyAll()方法被调用时,会唤醒在当前对象上等待的所有线程,使它们从阻塞状态转变为就绪状态。这样,所有等待中的线程都有机会去竞争获取对象的锁。
  2. 解决等待问题notifyAll()通常用于解决多线程之间的通信和协调问题。当某个条件得到满足时,调用notifyAll()可以通知所有等待该条件的线程。
  3. 谨慎使用:需要谨慎使用notifyAll(),因为唤醒所有线程可能会导致竞争和性能问题。在某些情况下,如果只有一个线程是合适的接收方,那么使用notify()来唤醒特定线程会更有效率。
  4. 必须在同步块中调用:就像notify()方法一样,notifyAll()也必须在同步块(synchronized块)内调用,以确保在对象上同步。通过同步块,确保在调用notifyAll()时,持有对象的锁。
  5. 竞争获取锁:被唤醒的线程会尝试获取对象的锁,一旦获得锁,它们将从wait()方法返回,并开始竞争执行。
  6. 释放锁:调用notifyAll()并不会释放对象锁,它仅唤醒等待的线程,这些线程会在合适的时机争夺锁。

2.2.3 notify 使用场景

notify()的使用场景:

  • 单个唤醒:当只需唤醒等待队列中的一个线程时,适合使用notify()。这种情况下,最多只有一个等待线程被唤醒,选择被唤醒的线程是不确定的。
  • 资源更新:当某个共享资源的状态发生变化时,并且只需要通知一个线程来处理这种变化时,可以使用notify()

notifyAll()的使用场景:

  • 多个唤醒:当需要唤醒所有等待线程来处理某个共享资源的变化时,应该使用notifyAll()。这种情况下,所有等待线程都会被唤醒。
  • 条件变更:当共享资源的状态发生变化对多个线程都有影响时,可以使用notifyAll()来通知所有等待线程,因为多个线程可能对资源的状态变化做出不同的响应。
  • 避免竞争问题:在某些情况下,使用notifyAll()可以避免因为只唤醒一个线程而导致的竞争问题,确保所有等待线程都能及时做出响应。

2.2.4 notify 的执行原理

notify()方法的执行原理:

  1. 当一个线程调用对象的notify()方法时,这个对象的监视器被激活。
  2. 在等待该对象的线程中,会有一个线程从等待队列中被选中,进入到锁定状态,并尝试重新获取对象的锁。
  3. 一旦获取到锁,该线程会从wait()方法返回,并继续执行。
  4. 其他等待该对象的线程仍然处于等待状态,需要重新竞争获取对象的锁来继续执行。

notifyAll()方法的执行原理:

  1. 当一个线程调用对象的notifyAll()方法时,所有等待该对象的线程将被唤醒。
  2. 被唤醒的线程会一起竞争对象的锁,以便能够继续执行。
  3. 每个被唤醒的线程尝试重新获取锁,一旦成功获取到锁,它们会从wait()方法返回,并接着执行后续代码。

2.2.5 notify和notifyAll 注意事项和要点:

  • 使用 notify和notifyAll 时需要先对调用的对象加锁,否则会报错
  • notify()方法和notifyAll()方法必须在同步块中(synchronized块)调用,以确保在调用这些方法时对象的锁处于正确状态。
  • 被唤醒的线程在获取到锁并从wait()方法返回后,可能需要检查等待期间的条件是否发生了变化,从而决定是否继续执行。

三、wait/nofity 经典使用方式

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;public class WaitNotify {static boolean flag = true;static Object  lock = new Object();public static void main(String[] args) {Thread waitThread = new Thread(new Wait(), "waitThread");waitThread.start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(new Notify(), "notifyThread").start();}//waitstatic class Wait implements Runnable {public void run() {synchronized (lock) {while (flag) {try {System.out.println(Thread.currentThread() + " flag is true" + new SimpleDateFormat("HH:mm:ss").format(new Date()));//没有释放资源 wait 等待lock.wait();} catch (InterruptedException e) {}}System.out.println(Thread.currentThread() + " flag is false" + new SimpleDateFormat("HH:mm:ss").format(new Date()));}}}static class Notify implements Runnable {public void run() {synchronized (lock) {while (flag) {System.out.println(Thread.currentThread() + "hold lock  notify" + new SimpleDateFormat("HH:mm:ss").format(new Date()));//资源准备好了,nofitylock.notifyAll();flag = false;try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}}}synchronized (lock) {System.out.println(Thread.currentThread() + "hold lock again. notify" + new SimpleDateFormat("HH:mm:ss").format(new Date()));try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}}}}
}

这是最经典的等待/通知的范式:但资源没有准备好时,wait 等待,当资源准备好了,notify 通知。这里也留一个思考题:这里的 flag 为什么不需要用 volatile 修饰,答案在这里:多线程一些基础知识准备以及 volatile 和 synchronized

四、总结

文章详细讲解了Java中wait和notify方法的使用方法和注意事项,通过这些方法实现线程间的协调与通信,并列举了常见的使用场景,如协调多个线程操作、实现生产者-消费者模型等。此外,文章还强调了wait和notify方法必须在同步块中调用,以确保线程安全。

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

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

相关文章

JDBC中的事务及其ACID特性

在JDBC&#xff08;Java Database Connectivity&#xff09;中&#xff0c;事务&#xff08;Transaction&#xff09;是指作为单个逻辑工作单元执行的一系列操作。这些操作要么全部执行&#xff0c;要么全部不执行&#xff0c;从而确保数据库的完整性和一致性。事务是现代数据库…

实战|记一次java协同办公OA系统源码审计

前言 因为笔者也是代码审计初学者&#xff0c;写得不好的地方请见谅。该文章是以项目实战角度出发&#xff0c;希望能给大家带来启发。 审计过程 审计思路 1、拿到一个项目首先要看它使用了什么技术框架&#xff0c;是使用了ssh框架&#xff0c;还是使用了ssm框架&#xff…

面试突击指南:Java基础面试题2

面向对象和集合 1. 面向对象和面向过程的区别 面向过程:面向过程的编程方式是分析解决问题的步骤,然后用函数把这些步骤一步一步地实现,并在使用的时候逐个调用。这种方式性能较高,因此在单片机和嵌入式开发中经常采用面向过程开发。 面向对象:面向对象的编程方式是把问…

C#基于SkiaSharp实现印章管理(2)

上一篇文章最后提到基于System.Text.Json能够序列化SKColor对象&#xff0c;但是反序列化时却无法解析本地json数据。换成Newtonsoft.Json进行序列化和反序列化也是类似的问题。   通过百度及查看微软的帮助文档&#xff0c;上述情况下需自定义转换类以处理SKColor类型数据的…

搜维尔科技:【研究】触觉手套比控制器更能带来身临其境、更安全、更高效的虚拟体验

自然交互可提高VR模拟的有效性。研究表明&#xff0c;触觉手套比控制器更能带来身临其境、更安全、更高效的虚拟体验。 以下是验证 医疗培训中的触觉技术 “ 95.5%的参与者表示触摸是 XR 教育的重要组成部分&#xff0c;90.9% 的参与者表示 XR 触觉将提供一个安全的学习场所。…

Python错误集锦:faker模块生成xml文件时提示:`xml` requires the `xmltodict` Python library

原文链接&#xff1a;http://www.juzicode.com/python-error-faker-exceptions-unsupportedfeature-xml-requires-the-xmltodict-python-library 错误提示&#xff1a; faker模块生成xml文件时提示&#xff1a; xml requires the xmltodict Python library Traceback (most r…

经典文献阅读之--MobileViT(轻量级、通用且移动友好的网络框架)

Tip: 如果你在进行深度学习、自动驾驶、模型推理、微调或AI绘画出图等任务&#xff0c;并且需要GPU资源&#xff0c;可以考虑使用UCloud云计算旗下的Compshare的GPU算力云平台。他们提供高性价比的4090 GPU&#xff0c;按时收费每卡2.6元&#xff0c;月卡只需要1.7元每小时&…

尚品汇-(七)

&#xff08;1&#xff09;在网关中实现跨域 全局配置类实现 包名&#xff1a;com.atguigu.gmall.gateway.config 创建CorsConfig类 Configuration public class CorsConfig {Beanpublic CorsWebFilter corsWebFilter(){// cors跨域配置对象CorsConfiguration configuration…

私有化部署ChatGPT:潜力与挑战

背景 以ChatGPT为代表的大语言模型服务在2023年初开始大规模爆发&#xff0c;AI技术从来没有如此接近普通民众。随着以Microsoft&#xff0c; Google&#xff0c; Meta &#xff08;Facebook&#xff09;为代表的科技巨头在AI技术领域相继发布重量级产品和服务&#xff0c;国内…

声场合成新方法:基于声波传播的框架

声场合成是指在房间内的麦克风阵列上&#xff0c;根据来自房间内其他位置的声源信号&#xff0c;合成每个麦克风的音频信号。它是评估语音/音频通信设备性能指标的关键任务&#xff0c;因为它是一种成本效益高的方法&#xff0c;用于数据生成以替代真实的数据收集&#xff0c;后…

武汉星起航:挂牌上海股权交易中心,自营店铺销售额迎飞跃式增长

2023年10月30日&#xff0c;对于武汉星起航电子商务有限公司而言&#xff0c;无疑是一个载入史册的重要日子。这一天&#xff0c;公司成功在上海股权托管交易中心挂牌展示&#xff0c;正式登陆资本市场&#xff0c;开启了全新的发展篇章。这一里程碑式的跨越&#xff0c;不仅彰…

RAG分块方法 从固定大小到自然语言处理分块——深入研究文本分块技术

发掘文本分块-准确的搜索结果和更智能的语言模型背后的秘诀&#xff0c;通过了解如何有效地分块文本&#xff0c;我们可以改进索引文档、处理用户查询和利用搜索结果的方式。准备好揭开文本分块的秘密了吗? 一、了解分块 分块是一种旨在嵌入尽可能少噪音的内容&#xff0c;同…

IDEA中Maven--下载安装自己适配的版本---理解

Maven解释&#xff1a; Maven是一个强大的项目管理工具和构建工具&#xff0c;主要用于Java项目。它能够帮助开发团队管理项目的依赖、构建项目、发布文档和报告&#xff0c;并能够自动化许多重复的任务。 Maven的主要作用包括&#xff1a; 依赖管理&#xff1a;Maven能够管理…

vue2面试题——路由

1. 路由的模式和区别 路由的模式&#xff1a;history&#xff0c;hash 区别&#xff1a; 1. 表象不同 history路由&#xff1a;以/为结尾&#xff0c;localhost:8080——>localhost:8080/about hash路由&#xff1a;会多个#&#xff0c;localhost:8080/#/——>localhost:…

【Elasticsearch】在es中实现mysql中的FIND_IN_SET查询条件

需求场景: 有个文章表里面有个type字段,它存储的是文章类型,有 1头条、2推荐、3热点、4图文等等 。 商品表中有一个type字段,储存的事商品类型例如:1.热销单品,2.品类TOP10,3.销量榜TOP10等等 它的type字段值很有可能是1,2,3,4 在mysql中实现语句 select * from produc…

6.2 通过构建情感分类器训练词向量

在上一节中&#xff0c;我们简要地了解了词向量&#xff0c;但并没有去实现它。在本节中&#xff0c;我们将下载一个名为IMDB的数据集(其中包含了评论)&#xff0c;然后构建一个用于计算评论的情感是正面、负面还是未知的情感分类器。在构建过程中&#xff0c;还将为 IMDB 数据…

第二期书生·浦语大模型实战营优秀项目一览

书生浦语社区于 2023 年年底正式推出了书生浦语大模型实战营系列活动&#xff0c;至今已有两期五批次同学参加大模型学习、实战&#xff0c;线上课程累计学习超过 10 万人次。 实战营特设项目实践环节&#xff0c;提供 A100 算力支持&#xff0c;鼓励学员动手开发。第 2 期实战…

【移动应用开发期末复习】第五/六章

系列文章 第一章——Android平台概述 第一章例题 第二章——Android开发环境 第二章例题 第三章 第三章例题 第四章 系列文章界面布局设计线性布局表格布局帧布局相对布局约束布局控制视图界面的其他方法代码控制视图界面数据存储与共享首选项信息数据文件SQLite数据库Content…

HarmonyOS Next开发学习手册——进程模型线程模型

进程模型 系统的进程模型如下图所示&#xff1a; 应用中&#xff08;同一包名&#xff09;的所有PageAbility、ServiceAbility、DataAbility、FormAbility运行在同一个独立进程中&#xff0c;即图中绿色部分的“Main Process”。 WebView拥有独立的渲染进程&#xff0c;即图中…

AI问答-ERP:理解 ERP / 我国ERP发展现状 / ERP软件有哪些 / 华为自研ERP

一、理解ERP 1.1、定义 ERP&#xff08;Enterprise Resource Planning&#xff09;是企业资源计划的缩写&#xff0c;它集成了企业各个业务领域&#xff0c;包括采购、销售、库存、生产制造、财务等多个方面&#xff0c;进行全面管理、智能决策的一种企业管理系统。 1.2、功…