博客
关于我
Java 并发之虚假唤醒
阅读量:341 次
发布时间:2019-03-04

本文共 2999 字,大约阅读时间需要 9 分钟。

什么是虚假唤醒?

在多线程编程中,虚假唤醒是一个常见的问题。它通常发生在多个线程等待同一条件满足时。如果条件在某个时间点被触发,所有处于等待状态的线程都会被唤醒。然而,只有其中一个线程能够获取所需的锁或资源进行操作,其余线程虽然被唤醒,但由于无法继续执行会陷入死循环或错误的处理流程。这种情况下,唤醒的线程没有实际意义,导致资源浪费和逻辑错误。

虚假唤醒在开发中的常见问题

在多线程环境中,判断线程是否需要阻塞(调用 wait())时,使用 if 判断会导致虚假唤醒。例如,当 product 小于等于 1 时,所有等待 product >= 1 的线程都会被唤醒。然而,只有一个线程能够获取锁并执行 wait(),其余未能获取锁的线程会被唤醒后继续执行,这样会导致这些线程在没有必要的情况下进行操作,引发逻辑错误。

if (product >= 1) {    wait();}

上述代码会导致虚假唤醒。解决方法是将 if 替换为 while,确保所有等待的线程只有在条件满足时才被唤醒,并且只有一个线程能够继续执行。

while (product >= 1) {    wait();}

为什么 if 会产生虚假唤醒

if 语句只会在条件满足时执行一次 wait(),然后立即继续执行后续代码。然而,如果在 if 语句执行后条件又再次满足,其他线程仍然会被唤醒并继续等待,但由于 if 语句已经执行过,后续的条件检查不会再次进行。这种情况下,其他线程虽然被唤醒,但由于条件不再满足,会陷入死循环或错误的处理逻辑。

如何产生虚假唤醒

while (product >= 1) 替换为 if (product >= 1) 就会产生虚假唤醒。if 语句只会在条件满足时唤醒所有等待的线程,但只允许一个线程获取锁并继续执行,其余线程虽然被唤醒,但由于条件不再满足,会继续等待或执行无效操作。

生产者消费者示例

以下是一个典型的生产者消费者场景,展示虚假唤醒的影响:

public class Test {    public static void main(String[] args) {        Clerk clerk = new Clerk();        Producer producer = new Producer(clerk);        Customer customer = new Customer(clerk);        new Thread(producer, "生产者A").start();        new Thread(customer, "消费者A").start();        new Thread(producer, "生产者B").start();        new Thread(customer, "消费者B").start();    }}class Clerk {    private int product = 0;    public synchronized void add() {        while (product >= 1) {            System.out.println(Thread.currentThread().getName() + "---还有货物:" + product);            try {                wait();            } catch (InterruptedException e) {                e.printStackTrace();            }        }        product += 1;        System.out.println(Thread.currentThread().getName() + " 补货完成,剩余货物:" + product);        notifyAll();    }    public synchronized void sale() {        while (product <= 0) {            System.out.println(Thread.currentThread().getName() + " 缺货中:" + product);            try {                wait();            } catch (InterruptedException e) {                e.printStackTrace();            }        }        System.out.println(Thread.currentThread().getName() + " 已补货,开始消费...");        product -= 1;        notifyAll();    }}class Producer implements Runnable {    private Clerk clerk;    public Producer(Clerk clerk) {        this.clerk = clerk;    }    @Override    public void run() {        for (int i = 0; i < 20; i++) {            try {                Thread.sleep(200);            } catch (InterruptedException e) {                e.printStackTrace();            }            clerk.add();        }    }}class Customer implements Runnable {    private Clerk clerk;    public Customer(Clerk clerk) {        this.clerk = clerk;    }    @Override    public void run() {        for (int i = 0; i < 20; i++) {            try {                Thread.sleep(200);            } catch (InterruptedException e) {                e.printStackTrace();            }            clerk.sale();        }    }}

在上述代码中,如果将 while 替换为 if,就会出现虚假唤醒的情况。例如,当 product 小于等于 1 时,所有等待的线程都会被唤醒,但只有一个线程能够获取锁并继续执行,其余线程虽然被唤醒,但由于条件不再满足,会继续等待或执行无效操作。

转载地址:http://lofh.baihongyu.com/

你可能感兴趣的文章
OpenCV与AI深度学习 | 基于YOLO11的车体部件检测与分割
查看>>
OpenCV与AI深度学习 | 基于YoloV11自定义数据集实现车辆事故检测(有源码,建议收藏!)
查看>>
OpenCV与AI深度学习 | 基于YOLOv8 + BotSORT实现球员和足球检测与跟踪 (步骤 + 源码)
查看>>
OpenCV与AI深度学习 | 基于YOLOv8实现高级目标检测和区域计数
查看>>
OpenCV与AI深度学习 | 基于YOLOv8的停车对齐检测
查看>>
OpenCV与AI深度学习 | 基于YoloV8的药丸/片剂类型识别
查看>>
OpenCV与AI深度学习 | 基于YOLO和EasyOCR从视频中识别车牌
查看>>
OpenCV与AI深度学习 | 基于图像处理的火焰检测算法(颜色+边缘)
查看>>
OpenCV与AI深度学习 | 基于拉普拉斯金字塔实现图像融合(步骤 + 代码)
查看>>
OpenCV与AI深度学习 | 基于机器视觉的磁瓦表面缺陷检测方案
查看>>
OpenCV与AI深度学习 | 基于深度学习的轮胎缺陷检测系统
查看>>
OpenCV与AI深度学习 | 如何使用YOLOv9分割图像中的对象
查看>>
OpenCV与AI深度学习 | 如何使用YOLOv9检测图片和视频中的目标
查看>>
OpenCV与AI深度学习 | 如何在 Docker 容器中使用 GPU
查看>>
OpenCV与AI深度学习 | 实战 | OpenCV中更稳更快的找圆方法--EdgeDrawing使用演示(详细步骤 + 代码)
查看>>
OpenCV与AI深度学习 | 实战 | OpenCV传统方法实现密集圆形分割与计数(详细步骤 + 代码)
查看>>
OpenCV与AI深度学习 | 实战 | OpenCV实现扫描文本矫正应用与实现详解(附源码)
查看>>
OpenCV与AI深度学习 | 实战 | 使用OpenCV和Streamlit搭建虚拟化妆应用程序(附源码)
查看>>
OpenCV与AI深度学习 | 实战 | 使用OpenCV确定对象的方向(附源码)
查看>>
OpenCV与AI深度学习 | 实战 | 使用YOLOv8 Pose实现瑜伽姿势识别
查看>>