博客
关于我
Java 并发之虚假唤醒
阅读量:355 次
发布时间: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/

你可能感兴趣的文章
Python出现 RuntimeError: dictionary changed size during iteration 的解决方法
查看>>
Python-numpy(2)数组的操作
查看>>
Python-Numpy的tile函数用法
查看>>
python-open函数
查看>>
Python-Pandas-将特定函数应用于给定级别-多索引数据框架
查看>>
python-queue队列通信
查看>>
python-ros No module named PyKDL
查看>>
python-social-auth 是否需要会话
查看>>
Python再获年度编程语言,微软或成最大赢家
查看>>
python-week5
查看>>
Python写表情包搜索工具(非常详细)零基础入门到精通,收藏这一篇就够了
查看>>
Python-【1】配置
查看>>
Python-从子文件夹执行绝对导入
查看>>
Python-刷新页面,直到元素可单击
查看>>
PYTHON-唤醒+VOSK+DEEPSEEK+离线合成-实现纯离线大模型问答
查看>>
Python-在Dash回调中重用函数
查看>>
PYTHON-基本数据类型-元祖类型,字典类型,集合类型
查看>>
python-外观模式
查看>>
Python-如何使用FastAPI和uvicorn.run而不阻塞线程?
查看>>
Python-字典
查看>>