重学java 43.多线程 多等待多唤醒案例

news2025/3/16 13:34:13

Fear never builds the future,but hope does.

                                                          —— 24.5.25

多等待多唤醒问题

        在多条线程同时消费同时等待时,会出现问题

BaoZiPu

package S77ThreadMoreWait;

/*
    count和flag可以定义成包装类,但要记得给count和flag手动赋值
    不然对于本案例来说,容易出现空指针异常
 */
public class BaoZiPu {
    // 包子的数目count
    private int count;
    // 是否有包子flag
    private boolean flag;

    public BaoZiPu() {
    }

    public BaoZiPu(int count, boolean flag) {
        this.count = count;
        this.flag = flag;
    }

    // getCount改成消费包子,直接输出包子数量count
    public void getCount() {
        System.out.println("消费了第"+count+"个包子");
    }

    // setCount改造成生产包子,count++
    public void setCount() {
        count++;
        System.out.println("生产了第"+count+"个包子");
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

生产product线程

package S77ThreadMoreWait;

// 实现Runnable接口
public class Product implements Runnable{
    BaoZiPu baoZiPu = new BaoZiPu();

    // 提供一个有参构造
    public Product(BaoZiPu baoZiPu){
        this.baoZiPu = baoZiPu;
    }
    @Override
    public void run() {
        // 定义一个死循环
        while(true) {
            try {
                Thread.sleep(100L);
            }catch (InterruptedException e){
                throw new RuntimeException(e);
            }


            // 同步代码块
            synchronized (baoZiPu){
                // 1.判断flag是否为true,如果是true,证明有包子,生产线程等待
                if(baoZiPu.isFlag()==true){
                    try{
                        baoZiPu.wait();
                    }catch(InterruptedException e){
                        throw new RuntimeException(e);
                    }
                }

                // 2.如果flag为false,证明没有包子,则要开始生产
                baoZiPu.setCount();
                // 3.改变flag为true
                baoZiPu.setFlag(true);
                // 4.唤醒所有等待线程
                baoZiPu.notify();
            }
        }
    }
}

消费consumer线程

package S77ThreadMoreWait;

public class Consumer implements Runnable{
    BaoZiPu baoZiPu = new BaoZiPu();

    // 提供一个有参构造
    public Consumer(BaoZiPu baoZiPu){
        this.baoZiPu = baoZiPu;
    }

    @Override
    public void run() {
        while(true) {
            try {
                Thread.sleep(100L);
            }catch (InterruptedException e){
                throw new RuntimeException(e);
            }

            // 同步代码块
            synchronized (baoZiPu){
                // 1.判断flag是否为false,如果是false,证明没有包子,消费线程等待
                if(baoZiPu.isFlag()==false){
                    // 抛出异常
                    try{
                        baoZiPu.wait();
                    }catch(InterruptedException e){
                        throw new RuntimeException(e);
                    }
                }

                // 2.如果flag为true,则要开始消费
                baoZiPu.getCount();
                // 3.改变flag为false,消费完了,没有包子了
                baoZiPu.setFlag(false);
                // 4.唤醒所有等待线程
                baoZiPu.notify();
            }
        }
    }
}

测试类

package S77ThreadMoreWait;


public class Demo216Test {
    public static void main(String[] args) {
        // 变成同一个对象
        BaoZiPu baoZiPu = new BaoZiPu();

        // 把baozipu对象分别传给两个线程中
        Product product = new Product(baoZiPu);
        Consumer consumer = new Consumer(baoZiPu);

        // 三条生产线程
        new Thread(product).start();
        new Thread(product).start();
        new Thread(product).start();
        // 三条消费线程
        new Thread(consumer).start();
        new Thread(consumer).start();
        new Thread(consumer).start();
    }
}

运行结果:会出现连续生产和连续消费的行为

         多条线程在同时等待时,上一条线程结束之后会随机唤醒一条线程,所以不能确定具体的顺序 

解决尝试 — 唤醒全部线程 notifyAll

   生产线程Product

package S77ThreadMoreWait;

// 实现Runnable接口
public class Product implements Runnable{
    BaoZiPu baoZiPu = new BaoZiPu();

    // 提供一个有参构造
    public Product(BaoZiPu baoZiPu){
        this.baoZiPu = baoZiPu;
    }
    @Override
    public void run() {
        // 定义一个死循环
        while(true) {
            try {
                Thread.sleep(100L);
            }catch (InterruptedException e){
                throw new RuntimeException(e);
            }


            // 同步代码块
            synchronized (baoZiPu){
                // 1.判断flag是否为true,如果是true,证明有包子,生产线程等待
                if(baoZiPu.isFlag()==true){
                    try{
                        baoZiPu.wait();
                    }catch(InterruptedException e){
                        throw new RuntimeException(e);
                    }
                }

                // 2.如果flag为false,证明没有包子,则要开始生产
                baoZiPu.setCount();
                // 3.改变flag为true
                baoZiPu.setFlag(true);
                // 4.唤醒所有等待线程
                baoZiPu.notifyAll();
            }
        }
    }
}

   消费线程Consumer

package S77ThreadMoreWait;

public class Consumer implements Runnable{
    BaoZiPu baoZiPu = new BaoZiPu();

    // 提供一个有参构造
    public Consumer(BaoZiPu baoZiPu){
        this.baoZiPu = baoZiPu;
    }

    @Override
    public void run() {
        while(true) {
            try {
                Thread.sleep(100L);
            }catch (InterruptedException e){
                throw new RuntimeException(e);
            }

            // 同步代码块
            synchronized (baoZiPu){
                // 1.判断flag是否为false,如果是false,证明没有包子,消费线程等待
                if(baoZiPu.isFlag()==false){
                    // 抛出异常
                    try{
                        baoZiPu.wait();
                    }catch(InterruptedException e){
                        throw new RuntimeException(e);
                    }
                }

                // 2.如果flag为true,则要开始消费
                baoZiPu.getCount();
                // 3.改变flag为false,消费完了,没有包子了
                baoZiPu.setFlag(false);
                // 4.唤醒所有等待线程
                baoZiPu.notifyAll();
            }
        }
    }
}

  运行结果:会出现连续生产和连续消费的行为

解决尝试 — 把 if 改成 while

 生产线程Product

package S77ThreadMoreWait;

// 实现Runnable接口
public class Product implements Runnable{
    BaoZiPu baoZiPu = new BaoZiPu();

    // 提供一个有参构造
    public Product(BaoZiPu baoZiPu){
        this.baoZiPu = baoZiPu;
    }
    @Override
    public void run() {
        // 定义一个死循环
        while(true) {
            try {
                Thread.sleep(100L);
            }catch (InterruptedException e){
                throw new RuntimeException(e);
            }


            // 同步代码块
            synchronized (baoZiPu){
                // 1.判断flag是否为true,如果是true,证明有包子,生产线程等待
                while (baoZiPu.isFlag()==true){
                    try{
                        baoZiPu.wait();
                    }catch(InterruptedException e){
                        throw new RuntimeException(e);
                    }
                }

                // 2.如果flag为false,证明没有包子,则要开始生产
                baoZiPu.setCount();
                // 3.改变flag为true
                baoZiPu.setFlag(true);
                // 4.唤醒所有等待线程
                baoZiPu.notifyAll();
            }
        }
    }
}

 消费线程Consumer

package S77ThreadMoreWait;

public class Consumer implements Runnable{
    BaoZiPu baoZiPu = new BaoZiPu();

    // 提供一个有参构造
    public Consumer(BaoZiPu baoZiPu){
        this.baoZiPu = baoZiPu;
    }

    @Override
    public void run() {
        while(true) {
            try {
                Thread.sleep(100L);
            }catch (InterruptedException e){
                throw new RuntimeException(e);
            }

            // 同步代码块
            synchronized (baoZiPu){
                // 1.判断flag是否为false,如果是false,证明没有包子,消费线程等待
                while (baoZiPu.isFlag()==false){
                    // 抛出异常
                    try{
                        baoZiPu.wait();
                    }catch(InterruptedException e){
                        throw new RuntimeException(e);
                    }
                }

                // 2.如果flag为true,则要开始消费
                baoZiPu.getCount();
                // 3.改变flag为false,消费完了,没有包子了
                baoZiPu.setFlag(false);
                // 4.唤醒所有等待线程
                baoZiPu.notifyAll();
            }
        }
    }
}

 运行结果:不会出现连续生产和连续消费的行为

总结:notifyAll和while要一起执行,才可以保证我们的代码不会出现错误情况

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

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

相关文章

C# MemoryStream流的详解与示例

文章目录 1. MemoryStream的基本介绍2. 创建MemoryStream对象无参构造函数带参构造函数 3. 向MemoryStream中写入数据写入字节数组写入字符串 4. 从MemoryStream中读取数据读取字节数组读取字符串 5. MemoryStream的实用辅助方法获取长度设置长度定位 6. 示例代码 在C#中&#…

JWT使用方法

目录 基础概念 依赖 生成令牌 工具类 控制层 解析令牌 工具类 网关过滤器 效果 基础概念 Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点…

局域网桌面监控软件哪个好用?良心推荐

如何有效地监控和管理内部员工的计算机使用行为,防范潜在的安全风险,提高工作效率,是众多企业管理者关注的焦点。 而一款优秀的局域网桌面监控软件无疑能为企业的IT治理提供有力支撑。 小编在此给大家推荐一款好用的局域网桌面监控软件——域…

stm32H743不要将主频设置到480MHz

0 问题描述 本文使用的stm32H743是V版本,支持最高480MHz的主频。但在将主频设置为480MHz之后,使用FDCAN的回环模式出现了各种接收不到的异常问题。经过一番排查,将主频修改到400MHz,同时降低芯片内部LDO输出电压后恢复了正常。 …

深入理解NumPy与Pandas【numpy模块及Pandas模型使用】

二、numpy模块及Pandas模型使用 numpy模块 1.ndarray的创建 import numpy as np anp.array([1,2,3,4]) bnp.array([[1,2,3,4],[5,6,7,8]]) print(a) #[1 2 3 4] print(b) #[[1 2 3 4][5 6 7 8]] 1.1使用array()函数创建 numpy.array(object, dtype None, copy True, ord…

域内攻击 ----->约束非约束委派攻击

在域中,除了我们常见的横向移动以外,还有很多攻击,像什么kerberoasting,委派攻击,NTLMrelay啊...... 还有很多(暂时只知道这些) 以前在一篇公众号看到的一个笑话也荟萃了网安的一些攻击手法&am…

设计模式在芯片验证中的应用——单例

一、单例模式 单例模式(Singleton)是一种创建型设计模式,能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点。验证环境配置(configuration)类、超时(timeout)处理类等可以使用单例实现。比如说验证环境需要在特定场景中监测特定接口上的超时事…

【机器学习300问】97、机器学习中哪些是凸优化问题,哪些是非凸优化问题?

在机器学习的领域中,多数模型的参数估计问题实质上可以转化为优化问题。鉴于机器学习模型的多样性,不同的模型会对应着不同的损失函数,进而形成各具特色的优化问题。了解优化问题的形式和特点,对于提升我们求解模型参数的效率和准…

在 Visual Studio 2022 (VS2022) 中删除 Git 分支的步骤如下

git branch -r PS \MauiApp1> git push origin --delete “20240523备份” git push origin --delete “20240523备份”

推荐五个线上兼职,在家也能轻松日入百元,适合上班族和全职宝妈

在这个瞬息万变的时代,你是否也曾考虑过在繁忙的工作之外,寻找一份兼职副业来补贴家用,同时保持生活的多样性?别急,现在就让我为你揭秘五个可靠的日结线上兼职岗位,助你轻松迈向财务自由之路! 一…

地质考察AR远程交互展示系统辅助老师日常授课

广东这片充满活力的土地,孕育了一家引领ARVR科技潮流的杰出企业——深圳华锐视点,作为一家专注于VR/AR技术研究与业务开发的先锋公司。多年来,我们不断突破技术壁垒,将AR增强现实技术与各行各业的实际需求完美结合,助力…

基于分块贝叶斯非局部均值优化(OBNLM)的图像去噪算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 块定义与相似度计算 ​4.2 贝叶斯框架下的加权融合 4.3 加权最小均方误差估计 5.完整程序 1.程序功能描述 基于分块贝叶斯非局部均值优化(OBNLM)的图像去噪算法matlab仿真&#xff0c…

JAVA线程池的线程数量配置

一 JAVA线程池的线程数量配置 关于线程的配置,线程数量配置多少这个问题呢?往往不同的环境与不同的线程中的代码决定其配置的线程的数量。 二 常见的线程量配置 最常见的方式根据线程中处理的代码划分为CPU密集型或IO密集型: CPU密集型&…

react实现table可拖拽表头(给react-jss样式传递参数、滚动条样式)

目录 react实现table可拖拽表头安装依赖resizableTitle / index.tsxdrapTable.tsx使用DragTable 组件滚动条样式效果 react实现table可拖拽表头 安装依赖 yarn add react-resizable yarn add react-jssresizableTitle / index.tsx import { createUseStyles } from react-js…

【电子学会】2023年09月图形化一级 -- 保护环境

保护环境 1. 准备工作 (1)删除角色小猫,添加角色Wizard,Bear-walking; (2)添加背景Desert和Forest。 2. 功能实现 (1)调整魔法师和熊的大小为50; &…

Linux文本处理三剑客(详解)

一、文本三剑客是什么? 1. 对于接触过Linux操作系统的人来说,应该都听过说Linux中的文本三剑客吧,即awk、grep、sed,也是必须要掌握的Linux命令之一,三者都是用来处理文本的,但侧重点各不相同,a…

MySQL---通用语法及分类

目录 一、SQL通用语法 二、 SQL分类 1.DDL 1.1 DDL数据库操作 1.2 DDL表操作---查询 1.3 DDL表操作---创建​编辑 1.4 DDL表操作---数据类型 1.5 DDL表操作---修改 1.6 DDL表操作---删除 1.7 DDL总结 2. 图形化界面工具DataGrip 2.1 创建 2.2 使用 3. DML 3.1 DML介绍 3.2 DM…

基于开源ATmega8 无感BLDC程序移植到ATmega328PB

基于开源ATmega8 无感BLDC程序移植到ATmega328PB 🔖基于Atmel Studio 7.0开发环境。🥕开源原项目资源地址:https://svn.mikrokopter.de/websvn/listing.php?repnameBL-Ctrl&path%2F&📍原理图和PCB资源 BL-Ctrl v2.0 in E…

【FPGA】VGA显示文字、彩条、图片——基于DE2-115

文章目录 前言一、VGA概述1.1 简述1.2 管脚定义1.3 VGA显示原理1.4 VGA时序标准1.5 VGA 显示模式及相关参数 二、VGA显示自定义的汉字字符2.1 点阵汉字生成2.2 生成BMP文件2.3 生成txt文件2.4 实现效果 三、VGA显示条纹3.1 实现流程3.2 实现效果 四、VGA输出一幅彩色图像4.1 bm…

从旅游广告联想到《桃花源记》

近日收到《长江头条网》等知名网络自媒体相邀,促我写点儿旅游题材的文案。虽说笔者游历过许多名山大川的绝美风景区,但那是在70岁之前的事儿了。如今年逾78岁,纵使有少许自有资本能够支持出游,可体力难撑,岂不是花钱买罪受吗?而且,写没有亲身经历过的事挺难,即便发表出…