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

你可能感兴趣的文章
phpMailer发送邮件
查看>>
PHPMailer发送邮件
查看>>
phpmailer发送邮件,可以带附件
查看>>
phpmailer的用法
查看>>
phpmyadmin 安装
查看>>
phpmyadmin导出数据库出现Fatal error: Cannot 'break' 2 levels in D:\phpstudy\WWW\phpMyAdmin
查看>>
phpmyadmin数据库建表及插入
查看>>
phpnow配置
查看>>
phprpc简单使用
查看>>
phpspider中当爬虫获取数据时如何去掉广告
查看>>
phpstorm 2016.3.3 激活
查看>>
phpstorm中Xdebug的使用
查看>>
phpstorm中使用svn版本控制器
查看>>
phpstorm配置php脚本执行
查看>>
PhpStorm配置远程xdebug
查看>>
phpstudy+iis搭建php项目
查看>>
phpStudy安装教程
查看>>
phpstudy搭建网站,通过快解析端口映射外网访问
查看>>
phpunit
查看>>
PHPUnit单元测试对桩件(stub)和仿件对象(Mock)的理解
查看>>