博客
关于我
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关键代码总览!从Zero到Hero必看的考点
查看>>
python3 robotframework+pycharm框架搭建
查看>>
Python3 shutil模块
查看>>
Python关于strftime函数详细解析 附实战代码
查看>>
Python3 tutorial day3
查看>>
python3 week4
查看>>
Python3 创建数组
查看>>
python3 套接字异常(a byte-like object is required not str)
查看>>
python3 实现用户三次输入错误记录黑名单并对该用户登录锁定-day1
查看>>
python3 文本变图片
查看>>
Python3 查看 com 组件的属性
查看>>
python3 案例分享--发邮件
查看>>
python3 模板库_Python3 标准库概览_Python教程_我爱模板网 - 提供下载各种免费建站资源,免费网站模板,免费网页特效,让你爱上建站!...
查看>>
Python3 求最大/小值及索引值 Numpy
查看>>
python3+django使用celery执行某些任务失败的解决方案
查看>>
Python3+Requests+Unittest实战系列【一】
查看>>
python3+requests+unittest实战系列【一】
查看>>
python3+requests+unittest实战系列【二】
查看>>
Python3+Requests+Unittest实战系列【二】
查看>>
python3----ASCII
查看>>