Java多线程生产者消费者说明等待唤醒机制问题和虚假唤醒问题

Java并发编程之Condition接口详解:多条件等待唤醒机制 Java的Condition接口为多线程编程提供了比传统wait()/notify()更精细的控制。Condition支持多个等待队列,允许线程在不同条件下等待和唤醒,避免了虚假唤醒问题。核心方法包括await()、signal()等,使用时必须配合Lock和while循环检查条件。典型应用如生产者-消费者模型,通过notFull和notEmpty两个Condition分别控制缓冲区状态。相比Object的监视器方法,Condition支持精确唤醒、多种超时控制和不可中断等待等高级特性,使线程协调更加灵活可 阅读详情

不用等待唤醒机制实现的生产者与消费者

代码

package com.hust.juc;

/*
 * 生产者和消费者案例
 */
public class TestProductorAndConsumer {

    public static void main(String[] args) {
        Clerk clerk = new Clerk();

        Productor pro = new Productor(clerk);
        Consumer cus = new Consumer(clerk);

        new Thread(pro, "生产者 A").start();
        new Thread(cus, "消费者 B").start();

        /*
         * new Thread(pro, "生产者 C").start(); new Thread(cus, "消费者 D").start();
         */
    }

}

// 店员
class Clerk {
    private int product = 0;

    // 进货
    public synchronized void get() {// 循环次数:0
        if (product >= 1) {
            System.out.println("产品已满!");

            /*try {
                this.wait();
            } catch (InterruptedException e) {
            }*/

        } else {
            System.out.println(Thread.currentThread().getName() + " : "
                    + ++product);
            //this.notifyAll();
        }

    }

    // 卖货
    public synchronized void sale() {// product = 0; 循环次数:0
        if (product <= 0) {
            System.out.println("缺货!");

            /*try {
                this.wait();
            } catch (InterruptedException e) {
            }*/
        } else {
            System.out.println(Thread.currentThread().getName() + " : "
                    + --product);
            //this.notifyAll();
        }
    }
}

// 生产者
class Productor implements Runnable {
    private Clerk clerk;

    public Productor(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
            }

            clerk.get();
        }
    }
}

// 消费者
class Consumer implements Runnable {
    private Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            clerk.sale();
        }
    }
}

运行结果

这里写图片描述

只要线程得到了CPU就会运行,缺货也会一直去缺货,很不科学,所以这个时候我们得用等待唤醒机制。

使用等待唤醒机制

代码

就是把上面的代码中await和notify的注释打开

运行结果

这里写图片描述

这个时候会发现都运行完了,但是程序并没有结束,这就是等待唤醒机制遗留问题。

解决等待唤醒机制的问题

我们分析一下,很容易想到是因为,await被唤醒之后是从await的地方继续执行,那么直接从else出走掉了,假如那是消费者的最后一轮循环,那么那个最后一轮循环的线程就结束了,生产者还有一轮或者多伦循环没有结束,生产者线程生产出的商品就没有线程去消费就会一直等待。

代码去掉else就解决了这个问题

package com.hust.juc;

/*
 * 生产者和消费者案例
 */
public class TestProductorAndConsumer {

    public static void main(String[] args) {
        Clerk clerk = new Clerk();

        Productor pro = new Productor(clerk);
        Consumer cus = new Consumer(clerk);

        new Thread(pro, "生产者 A").start();
        new Thread(cus, "消费者 B").start();

        /*
         * new Thread(pro, "生产者 C").start(); new Thread(cus, "消费者 D").start();
         */
    }

}

// 店员
class Clerk {
    private int product = 0;

    // 进货
    public synchronized void get() {// 循环次数:0
        if (product >= 1) {
            System.out.println("产品已满!");

            try {
                this.wait();
            } catch (InterruptedException e) {
            }

        }

        System.out
                .println(Thread.currentThread().getName() + " : " + ++product);
        this.notifyAll();
    }

    // 卖货
    public synchronized void sale() {// product = 0; 循环次数:0
        if (product <= 0) {
            System.out.println("缺货!");

            try {
                this.wait();
            } catch (InterruptedException e) {
            }
        }

        System.out
                .println(Thread.currentThread().getName() + " : " + --product);
        this.notifyAll();
    }
}

// 生产者
class Productor implements Runnable {
    private Clerk clerk;

    public Productor(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
            }

            clerk.get();
        }
    }
}

// 消费者
class Consumer implements Runnable {
    private Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            clerk.sale();
        }
    }
}

多线程下的虚假唤醒

说白了其实这还是“单线程“,因为只有一个线程生产一个线程消费,如果现在有两个线程去生产,两个线程去消费,那么就会出现虚假唤醒的情况。

虚假唤醒代码

把上面的代码中CD线程注释掉的打开就行。

运行截图

这里写图片描述

虚假唤醒问题分析

仔细考虑一下,假如现在消费者线程C执行,此时没有商品,消费者线程C等待,这个时候消费者线程D也拿到了CPU,也是没有商品的状态,也等待。这个时候一个生产者生产出了一个商品,此时唤醒了两个等待的消费者,往下执行,那么很自然就产生了-1等负数的情况。

解决办法就是把wait放在循环里

package com.hust.juc;

/*
 * 生产者和消费者案例
 */
public class TestProductorAndConsumer {

    public static void main(String[] args) {
        Clerk clerk = new Clerk();

        Productor pro = new Productor(clerk);
        Consumer cus = new Consumer(clerk);

        new Thread(pro, "生产者 A").start();
        new Thread(cus, "消费者 B").start();

        new Thread(pro, "生产者 C").start();
        new Thread(cus, "消费者 D").start();

    }
}

// 店员
class Clerk {
    private int product = 0;

    // 进货
    public synchronized void get() {// 循环次数:0
        while (product >= 1) {
            System.out.println("产品已满!");

            try {
                this.wait();
            } catch (InterruptedException e) {
            }

        }

        System.out
                .println(Thread.currentThread().getName() + " : " + ++product);
        this.notifyAll();
    }

    // 卖货
    public synchronized void sale() {// product = 0; 循环次数:0
        while (product <= 0) {
            System.out.println("缺货!");

            try {
                this.wait();
            } catch (InterruptedException e) {
            }
        }

        System.out
                .println(Thread.currentThread().getName() + " : " + --product);
        this.notifyAll();
    }
}

// 生产者
class Productor implements Runnable {
    private Clerk clerk;

    public Productor(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
            }

            clerk.get();
        }
    }
}

// 消费者
class Consumer implements Runnable {
    private Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            clerk.sale();
        }
    }
}

运行截图

这里写图片描述

这个才会得到解决。

虚假唤醒详解 虚假唤醒详解        接触过多线程编程的朋友们或多或少都听说过虚假唤醒这一术语,我在百度上看了很多相关的帖子,说的都不是很清楚,于是写下这篇文章,希望能对大家有些裨益。 虚假唤醒是什么        首先我们来谈谈虚假唤醒到底是什么。在这里我给出一个自己的定义,帮助大家去理解虚假唤醒 虚假唤醒是一种现象,它只会出现在多线程环境中,指的是在多线程环境下,多个线程等 阅读详情

相关推荐

Transformer在CV里太耗算力?手把手拆解Restormer的MDTA与GDFN模块

本文深度解析了CVPR2022最佳论文提名作品Restormer的创新模块MDTA与GDFN,有效解决了Transformer在计算机视觉领域的高计算复杂度问题。通过通道注意力降维和门控前馈网络设计,Restormer在图像恢复重建任务中显著降低计算量,同时保持高性能,为实时图像处理提供了可行方案。

weixin_30258901的博客 445

Java线程虚假唤醒是什么、如何避免?

一、什么是虚假唤醒? 多线程环境下,有多个线程执行了wait()方法,需要其他线程执行notify()或者notifyAll()方法去唤醒它们,假如多个线程都被唤醒了,但是只有其中一部分是有用的唤醒操作,其余的唤醒都是无用功;对于不应该被唤醒的线程而言,便是虚假唤醒。 比如:仓库有货了才能出库,突然仓库入库了一个货品;这时所有的线程(货车)都被唤醒,来执行出库操作;实际上只有一个线程(货车)能执行出库操作,其他线程都是虚假唤醒。 下图为JAVA8中对wait()方法虚假唤醒的介绍: 意思是:虚假唤醒是可能

健身变秃,coding变强 5587

1 FFmpeg从入门到精通-FFmpeg简介

1.FFmpeg简介 1.1 FFmpeg的定义 1.2 FFmpeg的历史 1.3 FFmpeg的基本组成 1.4 FFmpeg的编解码工具ffmpeg 1.5 FFmpeg的播放器ffplay 1.6 FFmpeg的多媒体分析器ffprobe 1.7 FFmpeg编译(1)FFmpeg之Windows平台编译(2)FFmpeg之Linux平台编译(3)FFmpeg之OS X平台编译 1.8 FFmpeg编码支持与定制(1)FFmpeg的编码器支持(2)FFmpe

migu123的专栏 3685

java - 多线程情境下产生的wait()虚假唤醒问题

一,是多线程环境(有多个线程拿到了同一个对象锁,并进行了wait()阻塞等待)。二,是拥有该对象锁的线程调用了notifyAll()方法。如果在多线程环境中满足了以上两个条件就有可能造成wait()虚假唤醒。

qq_61777435的博客 779

SCL3400-D01双轴高精度倾角计

简介 两轴高精度加速度计,提供两个模式。 ±30° with 10 Hz LPF ±90° with 40 Hz LPF Resolution up to 32768 LSB/g Ultra-low 0.0009°/√Hz noise density 这款传感器是替换之前村田的停产的模拟量系列的芯片,正在测试中,具体效果大伙可以评测。 一、引脚连接图 引脚图兼容SCL3300 二、SPI接口 1.SCL3300和SCL3400略有区别如下图 左边为SCL3...

专注惯导领域 3807

Ubuntu桌面消失?可能是lightdm配置在捣鬼:详细解析/etc/lightdm.conf的优先级机制与实战修复

本文详细解析了Ubuntu系统因lightdm显示管理器配置错误导致桌面消失的问题。通过分析/etc/lightdm.conf的优先级加载机制,提供了从systemctl诊断、调试模式运行到实战修复配置语法错误(如缺失[Seat:*]组)的完整方案。文章还探讨了在系统无法启动时使用chroot或恢复模式进行高级救援,并对比了lightdm与gdm3在NVIDIA驱动环境下的不同表现,帮助用户高效恢复图形界面。

weixin_29313547的博客 420

Java中的虚假唤醒(Spurious Wakeup)

Java中的虚假唤醒(Spurious Wakeup)

daweias的博客 868

学 Simulink—— 基于高频方波电压注入的表贴式永磁电机无感控制仿真

本文手把手教授基于Simulink的高频方波电压注入法实现表贴式永磁同步电机(SPMSM)无传感器控制。针对表贴式电机微弱凸极特性(Lq/Ld≈1.05-1.15),详解方波注入相比正弦注入的优势:通过5kHz方波(PWM频率半值)集中能量提升信噪比,并采用相邻周期电流直接相减的零延迟分离技术。系统架构包含高频注入器、无滤波器信号分离模块、锁相环观测器和磁饱和极性辨识等关键环节,提供参数设置经验(如注入幅值10-15%Vdc)和典型性能指标(位置误差<0.2rad)。特别强调工程实现要点:PWM同步时

xiaoheshang_123的博客 167

java 多线程中虚假唤醒

多线程环境的编程中,我们经常遇到让多个线程等待在一个条件上,等到这个条件成立的时候我们再去唤醒这些线程,让它们接着往下执行代码的场景。假如某一时刻条件成立,所有的线程都被唤醒了,然后去竞争锁,因为同一时刻只会有一个线程能拿到锁,其他的线程都会阻塞到锁上无法往下执行,等到成功争抢到锁的线程消费完条件,释放了锁,后面的线程继续运行,拿到锁时这个条件很可能已经不满足了,这个时候线程应该继续在这个条件上阻塞下去,而不应该继续执行,如果继续执行了,就说发生了虚假唤醒。在这里我给出一个自己的定义,用来理解虚假唤醒。

Primary_wind的专栏 653

UNet 改进(19):与ASPP结合的图像分割模型

这个代码实现了一个改进版的UNet模型,在传统的UNet架构基础上,引入了ASPP模块来增强模型捕获多尺度上下文信息的能力。​​UNet主干网络​​:经典的编码器-解码器结构,带有跳跃连接​​ASPP模块​​:位于瓶颈层,用于提取多尺度特征。

2401_82355416的博客 877

STM32入门教程:智能储物柜控制

在这个例子中,我们将使用一个4x4的储物柜,共有16个储物格。我们了解了硬件准备、储物柜电路设计、STM32的基本配置、储物柜控制代码的编写以及测试和调试等方面的知识。根据我们的电路设计,我们需要将一些引脚配置为输出,并将其他引脚配置为输入。在本教程中,我们将使用STM32微控制器来实现一个基本的智能储物柜控制系统。在主函数中,我们将不断循环读取传感器数据,并根据数据的变化来控制储物柜的开关和显示相关信息。这个函数将读取人体感应传感器、温湿度传感器和光照传感器的数据,并将数据存储到相应的变量中。

m0_75244442的博客 658

Java 生产者消费者案例——等待唤醒机制、虚假唤醒

wait() \ notify() \ notifyAll()这三方法必须在synchronized方法或synchronized同步代码块中执行,因为这样就说明currentThread已经获得了对象的monitor。 wait()使得当前线程释放已获得的对象monitor,并陷入一种等待。这种等待必须依靠别的获得同一个对象monitor的线程来调用notify() \ notifyAll()才会重新唤醒,但重新唤醒后需要继续执行没执行完的同步代码,而执行同步代码的前提是获得被调用成员方法的对象的moni

anlian523的博客 899

7.26华为OD机试真题 新系统 - 炸弹人的雷区数量 (Java/Py/C/C++/Js/Go)

7.26华为OD机试真题 新系统 - 炸弹人的雷区数量 (Java/Py/C/C++/Js/Go)

算法大师! 790

关于多线程中的虚假唤醒的原因和解决,以及分别用Lock锁和阻塞队列实现消费者生产者案例

虚假唤醒: 有这么一种情况,在一个线程调用wait()进行等待时,被其他线程所唤醒,但是这时同时有另外的线程也被唤醒了,它抢先运行后,使得原来的唤醒条件又不满足了,这时就悲剧了,这个线程就称为被虚假唤醒了。 有人说,它所在的线程是加锁的,唤醒后不是依然不能运行吗,是的,但是它被唤醒了,等到获取锁后,依然会运行。 解决: 所以为了避免这种情况,只好用while循环避免虚假唤醒。(因为if只判断一...

BestQiang的博客 1万+

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Rhyme/Java多线程之生产者消费者问题(等待唤醒机制、虚假唤醒、锁机制)

Java多线程之生产者消费者问题(等待唤醒机制、虚假唤醒、锁机制) 本篇博客由浅入深,先从最基础的生产者消费者问题讲起,然后依次深入 普通的生产者消费者问题 首先我们有一个店员类,模拟进货和卖货操作,店里的商品数量最多为10个 注意这里我们给进货和卖货方法都加了synchronized的关键字 生产者线程,负责生产商品 消费者线程,负责消费商品 好,然后我们先看运

RhymeChiang 510

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

生产者消费者问题虚假唤醒

生产者消费者问题演示及解决 问题一: package com.mock; import org.junit.jupiter.api.Test; public class TestProducerAndConsumer { @Test public void test() { Product p = new Product(); new Threa...

周莫客的博客 295

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

生产者与消费者

生产者与消费者,虚假唤醒

weixin_45794641的博客 4276

线程中的虚假唤醒

因为if只进行一次判断,假设当+1抢到了资源, 此时的num=1,其他三个线程进行资源抢夺,即使此时+1*抢到了资源,但是判断后,无法进行下面的代码,就沉睡下去,再当+1抢到资源时,就不会再从开始进行if判断了,从而会直接执行下面的++代码,使得num的值变为了2,导致线程不安全的情况,造成了虚假唤醒。扩容时,容量同样会增加为原来的1.5倍,并且所有键值对会重新哈希到新的位置上。中断和虚假唤醒是可能产生的,所以要用loop循环,if只判断一次,while是只要唤醒就要拉回来再判断一次。

qq_62984376的博客 1225

Java并发编程(五):生产者与消费者之虚假唤醒

生产者与消费者 场景 生产者不断生成产品,并交给售货员,消费者不断从售货员那里消费产品 利用代码实现: public class TestProductorAndConsumer { public static void main(String[] args) { Clerk clerk = new Clerk(); Productor prod...

Jerry1ee 303

JUC学习之生产者消费者案例-synchronized-虚假唤醒

本文为看视频学习记录,若有错误请指正!在实际开发过程中,我们可以将添加和创建数据的线程看做是生产者,删除和销毁数据的线程看做是消费者。若不用等待唤醒机制,假设生产者过快,消费者跟不上,那么会导致数据的丢失;                                  假设消费者过快,生产者跟不上,那么可能会导致重复的数据或者是错误的数据;synchronized写法-等待唤醒public c...

dyt443733328的博客 545

Java多线程等待唤醒机制万字深度解析

等待唤醒机制是Java多线程编程中一种重要的线程间协作方式,它允许线程在特定条件下暂停执行(等待),并在条件满足时被其他线程唤醒继续执行。这种机制解决了线程间的协调和通信问题,避免了忙等待(busy-waiting)造成的资源浪费。始终在循环中检查条件- 防止虚假唤醒先修改条件再通知- 确保被唤醒线程能看到最新状态使用notifyAll()的保守策略- 除非确定只有一个线程在等待避免嵌套锁- 预防死锁及时释放锁- 减少锁持有时间。

油墨香^-^的博客 917

多线程下的生产者与消费者模式及(notify()与signal()唤醒的使用和区别)

1. 问题的起源 用现实中的快餐店:生产一个汉堡消费一个汉堡为例 代码如下 // 多线程下的生产者与消费者模式 public class bounded_buffer_Problem { public static void main(String[] args) { HamburgerShop hamburgerShop = new HamburgerShop(); // 开启一个生产者线程,for循环1000次,生产1000个汉堡 new Thr

qq_43564410的博客 1310

6.JUC-线程的虚假唤醒-生产者和消费者

一)经典案例生产者和消费者 存在问题: 在经典的案例生产者和消费者中存在了一个很大的问题,当生产者生产的消息过快或者是消费者消费的消息过快容易出现数据丢失的问题。 解决方法:等待唤醒机制 演示生产者消费者案例 /** * @project_name:juc * @date:2019/9/27:21:06 * @author:shinelon * @Describe:生产者和消费者案例 *...

失忆๑的博客 183

认识多线程:等待唤醒机制

package com.pccc.pactera.juc01; public class TestProducterAndConsumer { public static void main(String[] args) { Clerk clerk = new Clerk(); Porductor p = new Porductor(clerk); Consumer c = ne...

woailuo23的博客 205

Java多线程等待唤醒机制详解与最佳实践

多线程编程中的等待唤醒机制是实现线程间高效协作的核心技术。通过Object类的wait/notify方法或Lock框架的Condition接口,线程可以在条件不满足时主动释放CPU资源,待条件满足时被精确唤醒。这种机制有效解决了传统轮询带来的性能损耗,广泛应用于生产者-消费者模式、线程池任务协调等并发场景。文章深入解析了等待唤醒的标准范式,包括如何避免虚假唤醒、处理丢失通知等典型问题,并对比了synchronized与Lock两种实现方式的差异。针对Java并发编程中的热点问题,特别介绍了Condition

weixin_29305337的博客 258
上一篇: 2017阿里算法编程题--数组分片问题
下一篇: IntelliJ IDEA 中 右键运行时没有run;新建时,选项没有Java class的解决方法和具体解释
杜鲁门
博客等级 码龄11年 219粉丝 316原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值