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

你可能感兴趣的文章
phpWhois 项目推荐
查看>>
Redis事务详解,吃透数据库没你想的那么难
查看>>
phpwind部署问题
查看>>
PHP_CodeIgniter Github实现个人空间
查看>>
php_crond:一个基于多进程的定时任务系统-支持秒粒度的任务配置
查看>>
PHP__call __callStatic
查看>>
PHP——修改数据库1
查看>>
PHP——封装Curl请求方法支持POST | DELETE | GET | PUT 等
查看>>
PHP——底层运行机制与原理
查看>>
php一句话图片运行,【后端开发】php一句话图片木马怎么解析
查看>>
PHP三方登录,移动端与服务端交互
查看>>
Redis事务深入解析和使用
查看>>
PHP上传文件大小限制的调整 Nginx 413 Request Entity Too Large
查看>>
php上传文件找不到临时文件夹
查看>>
PHP下curl用法分析
查看>>
php与web服务器关系
查看>>
redis事务操作
查看>>
php中0,空,null和false的区别
查看>>
PHP中array_merge和array相加的区别分析
查看>>
PHP中Closure::bindTo的用法分析
查看>>