wait/notify使用详解

news2025/1/8 18:36:56

1. 使用注意事项

  1. wait/notify(All)可用于线程间(线程数量>3)通信

  2. 永远在synchronized方法或对象里使用wait/notify(All),不然JVM报java.lang.IllegalMonitorStateException

  3. 永远在while循环里使用wait,防止其他原因改变先前判断条件

  4. 永远在线程间共享对象(生产者消费者中为缓冲区队列)上使用wait/notify(All)

  5. 多线程间协作,更倾向于使用notifyAll,唤醒在该对象上等待的全部线程

2. 实现生产者消费者

public class ProducerConsumerDemo {

    public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<>();
        int maxSize = 10;

        Thread producer = new Thread(new Producer(queue, maxSize));
        producer.setName("Producer");

        Thread consumerOne = new Thread(new Consumer(queue, maxSize));
        consumerOne.setName("ConsumerOne");

        Thread consumerTwo = new Thread(new Consumer(queue, maxSize));
        consumerTwo.setName("ConsumerTwo");

        producer.start();
        consumerOne.start();
        consumerTwo.start();
    }
}

class Producer implements Runnable {

    private Queue<Integer> queue;
    private int maxSize;

    public Producer(Queue<Integer> queue, int maxSize) {
        this.queue = queue;
        this.maxSize = maxSize;
    }

    @Override
    public void run() {
        Random random = new Random();
        while (true) {
            synchronized (queue) {
                while (queue.size() == maxSize) {
                    try {
                        System.out.println("Queue is full, Producer " + Thread.currentThread().getName() + " waiting");
                        queue.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                int value = random.nextInt();
                System.out.println("Producer " + value);
                queue.add(value);
                queue.notifyAll();

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

class Consumer implements Runnable {

    private Queue<Integer> queue;
    private int maxSize;

    public Consumer(Queue<Integer> queue, int maxSize) {
        this.queue = queue;
        this.maxSize = maxSize;
    }

    @Override
    public void run() {
        while (true) {
            // synchronized方法或对象里使用wait/notify(All),不然JVM报java.lang.IllegalMonitorStateException
            synchronized (queue) {
                // while不可改为if,如果改为if,则可能抛出java.util.NoSuchElementException
                // 当前等待的线程被唤醒时,但是由于其他消费者刚好消费完,使得队列为空
                // 此时如果不重新判断队列为空,代码继续向下执行queue.remove势必抛出java.util.NoSuchElementException
                // 若放入while循环中重新判断条件,若条件不满足,线程则继续挂起,无影响
                while (queue.isEmpty()) {
                    try {
                        System.out.println("Queue is empty, Consumer " + Thread.currentThread().getName() + " waiting");
                        queue.wait(); // 在线程间共享对象(生产者消费者中为缓冲区队列)上使用wait/notify(All)
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + " consume " + queue.remove());
                queue.notifyAll(); // 多线程间协作,更倾向于使用notifyAll,唤醒在该对象上等待的全部线程

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

执行结果:

Queue is empty, Consumer ConsumerOne waiting
Queue is empty, Consumer ConsumerTwo waiting
Producer -1203276467
Producer 626398544
Producer -108152878
Producer -705401944
ConsumerTwo consume -1203276467
ConsumerTwo consume 626398544
ConsumerTwo consume -108152878
ConsumerTwo consume -705401944
Queue is empty, Consumer ConsumerTwo waiting
Queue is empty, Consumer ConsumerOne waiting
Producer -1487489511
Producer 420807949
Producer 428506738
ConsumerOne consume -1487489511
ConsumerTwo consume 420807949
ConsumerTwo consume 428506738
Queue is empty, Consumer ConsumerTwo waiting
Queue is empty, Consumer ConsumerOne waiting
Producer 1411031303
Producer 1426589341
Producer -1307293143
Producer -970522822
Producer -2074755062
ConsumerOne consume 1411031303
ConsumerOne consume 1426589341

3. wait/nofity内部运行细节

  1. 使用wait()、notify()、notifyAll()时需要先对对象加锁
  2. 调用wait()方法后,线程状态由RUNNING变为WAITTING,并将当前线程放置到对象的等待队列
  3. notify()、notifyAll()方法调用后,等待线程依旧不会从wait()返回,需要调用notify()、notify()的线程释放锁后,等待线程才有机会从wait()返回
  4. notify()、notifyAll()方法将线程从等待队列移到同步队列(前者移1个,后者移全部),被移动的线程由WAITTING变为BLOCKED
  5. 从wait()方法返回的前提是获得了调用对象的锁[这也是3)中为啥说有机会返回的原因]

在这里插入图片描述

4. sleep与wait的区别

sleep是Thread中定义的方法, wait是Object中定义的方法;

可以在任何地方调用线程对象的sleep方法,wait方法只能在同步代码块或同步方法中调用;

调用线程对象的sleep方法后,不释放锁,调用对象的wait方法,释放对象获得的锁

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

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

相关文章

直流有刷电机的电路分析

这里写目录标题 H桥改进后的电路L298N原理图野火的电机驱动板MOS管野火的原理图 H桥 当 Q1 和 Q4 导通时&#xff0c;电流将经过 Q1 从左往右流过电机&#xff0c;在经过 Q4 流到电源负极&#xff0c;这时图中电机可以顺时针转动。 当 Q3 和 Q2 导通时&#xff0c;电流将经过 Q…

【AI算法学习】基于AutoEncoder的生成对抗网络

基于AutoEncoder的生成对抗网络&#xff1a;VAE-GAN AutoEncoderVAEGANVAE-GANDCGANInfoGANss-InfoGAN论文链接 " 生成模型&#xff08;Generative modeling&#xff09;"已成为机器学习的一个较为广泛的领域。在图像这种流行数据上&#xff0c;每张图像都有数千数万…

服务(第七篇)nginx优化

隐藏版本号&#xff1a; 方法①&#xff1a; 修改配置文件&#xff1a; 检测&#xff1a;版本号没有了 方法②&#xff1a; 首先修改nginx.h文件&#xff0c;修改版本号和服务名&#xff1a; 然后切换到/opt/nginx-1.18.0进行编译安装&#xff1a; 安装后进入nginx.conf进行…

Redis缓存实战(2)

目录 缓存定义 Redis缓存实战 1删除缓存还是更新缓存&#xff1f; 2如何保证缓存与数据库的操作同时成功或者失败&#xff1f; 3先操作数据库还是缓存&#xff1f; 缓存问题 缓存穿透 缓存雪崩 缓存击穿 缓存定义 缓存&#xff08;Cache)是数据交换的缓冲区&#xff0…

微信小程序自动化测试实战教程,框架源码应有尽有

目录 1. 微信小程序自动化测试介绍 2. 搭建微信小程序自动化测试框架 步骤1&#xff1a;选择测试工具 步骤2&#xff1a;搭建测试环境 步骤3&#xff1a;编写测试脚本 步骤4&#xff1a;执行测试 3. 实现微信小程序自动化测试的关键技术 技术1&#xff1a;微信小程序自动…

Netty:常见的面试题和答案

1. 什么是Netty&#xff1f; 答&#xff1a;Netty是一个高性能的网络编程框架&#xff0c;基于NIO的非阻塞式IO模型&#xff0c;可以帮助开发者快速开发高性能、高可靠性的网络应用程序。 2. Netty的核心组件有哪些&#xff1f; 答&#xff1a;Netty的核心组件包括&#xff…

GPT-3模型简单介绍

目录 一、概要 二、深入扩展 一、概要 与T5模型( Text-to-Text Transfer Transformer&#xff0c;详见文末链接 &#xff09;相似&#xff0c;OpenAI提出的GPT-3模型&#xff08;第三代GPT&#xff09;也是通过将不同形式的自然语言处理任务重定义为文本生成实现模型的通用化。…

【面试】一文读懂Java类加载全过程

文章目录 一、概述1. 类加载器2. 加载阶段3. 验证阶段4. 准备阶段5. 解析阶段6. 初始化阶段类加载过程总结 二、相关问题Q: 什么是类加载&#xff1f;Q: Java中有哪些类加载器&#xff1f;Q: 类加载的过程包括哪些步骤&#xff1f;Q: 类加载器的双亲委派模型是什么&#xff1f;…

告别StringUtil:使用Java的全新String API优化你的代码

前言 Java编程语言每一次主要更新&#xff0c;都引入了许多新功能和改进。 并且在String 类中引入了一些新的方法&#xff0c;能够更好地满足开发的需求&#xff0c;提高编程效率。 repeat(int count)&#xff1a;返回一个新的字符串&#xff0c;该字符串是由原字符串重复指定…

Jetson TX1 /TX2 对比介绍

大家好&#xff0c;我是虎哥&#xff0c;经过一段时间的整理&#xff0c;针对TX1/TX2这些看起来已经落伍的产品&#xff0c;如何找到合适的应用场景&#xff0c;我也整体上做了一些了解好调研。现在由于资料很多都比较老了&#xff0c;有些表述也有些前后表述不一&#xff0c;所…

ClickHouse环境搭建

目录 1 ClickHouse 的安装1.1 准备工作1.1.1 确定防火墙处于关闭状态1.1.2 CentOS 取消打开文件数限制1.1.3 安装依赖1.1.4 CentOS 取消 SELINUX 1.2 单机安装1.2.1 在 hadoop102 的/opt/software 下创建 clickhouse 目录1.2.2 将安装文件上传到 hadoop102 的software/clickhou…

Baumer工业相机堡盟工业相机如何联合BGAPISDK和OpenCV实现图像的伽马变换校正算法增强(C++)

Baumer工业相机堡盟工业相机如何联合BGAPISDK和OpenCV实现图像的伽马变换校正算法增强&#xff08;C&#xff09; Baumer工业相机Baumer工业相机使用图像算法增加图像的技术背景Baumer工业相机通过BGAPI SDK联合OpenCV使用图像增强算法1.引用合适的类文件2.BGAPI SDK在图像回调…

第三章 作业(7BF)【计算机系统结构】

第三章 作业&#xff08;7BF&#xff09;【计算机系统结构】 前言推荐第三章 作业&#xff08;7BF&#xff09;71115鲲鹏流水线调研华为鲲鹏处理器ARM体系的总体思想ARM的流水线结构 最后 前言 2023-4-10 18:49:41 以下内容源自《【计算机系统结构】》 仅供学习交流使用 推荐…

上一次c语言多文件处理代码的改进和总结

先看看上一次的文件&#xff1a; (3条消息) 认真复习c语言1_穿花云烛展的博客-CSDN博客 对于有重复结构体定义但是并不会报错&#xff0c;只是难以修改而已&#xff1a;为了解决这个一改就要改两次的情况&#xff0c;这里有一个解决方案&#xff1a; 上面的代码是可以运行的&…

常见的Web攻击技术

文章目录 前言HTTP 不具备必要的安全功能在客户端即可篡改请求针对 Web 应用的攻击模式 因输出值转义不完全引发的安全漏洞跨站脚本攻击XSSXSS实例 SQL 注入攻击实例 HTTP 首部注入攻击HTTP 首部注入攻击案例HTTP 响应截断攻击 因会话管理疏忽引发的安全漏洞会话劫持会话固定攻…

Java企业级开发学习笔记(2.2)利用MyBatis实现CRUD操作

该文章主要为完成实训任务&#xff0c;详细实现过程及结果见【http://t.csdn.cn/ajSEO】 文章目录 一、准备工作二、查询表记录2.1 在映射器配置文件里引入结果映射元素2.2 添加按姓名查询用户记录功能2.2.1 添加按姓名查询的映射语句2.2.2 添加按姓名查询用户记录的测试方法2.…

17、嵌入式Servlet容器

文章目录 1、切换嵌入式Servlet容器2、定制Servlet容器 【尚硅谷】SpringBoot2零基础入门教程-讲师&#xff1a;雷丰阳 笔记 路还在继续&#xff0c;梦还在期许 1、切换嵌入式Servlet容器 ● 默认支持的webServer ○ Tomcat, Jetty, or Undertow ○ ServletWebServerApplicati…

C++linux高并发服务器项目实践 day7

Clinux高并发服务器项目实践 day7 进程间通信匿名管道管道的特点匿名管道的使用创建匿名管道查看管道缓冲大小命令查看管道缓冲大小函数匿名管道通信案例 管道的读写特点 有名管道有名管道的使用写FIFO管道读FIFO管道总结有名管道实现简单版聊天功能 进程间通信 进程是一个独立…

SRv6实践项目(五):ONOS控制平面实现控制

在先前的几个小结中&#xff0c;一共了解了&#xff1a; p4的编译过程p4runtime的实现原理NDP协议的简单工作流程YANG模型的定义以及用处基于YANG的配置和状态的读写 一共实现了&#xff1a; Mininet拓扑创建p4的基本框架编写对数据平面进行订阅以实现状态读取对数据平面进行…

OJ系统刷题 第十一篇(重点题)

13463 - 折点计数&#xff08;难题&#xff01;重点题&#xff01;&#xff09; 时间限制 : 1 秒 内存限制 : 128 MB 给定 n 个整数表示一个商店连续 n 天的销售量。 如果某天之前销售量在增长&#xff0c;而后一天销售量减少&#xff0c;则称这一天为折点&#xff0c;反过来…