java假唤醒_JAVA线程虚假唤醒

线程虚假唤醒问题描述

​ 在JDK API文档中,关于Object类的wait()方法有这样一句话描述"线程也可以唤醒,而不会被通知,中断或超时,即所谓的虚假唤醒 。 虽然这在实践中很少会发生,但应用程序必须通过测试应该使线程被唤醒的条件来防范,并且如果条件不满足则继续等待",如下图所示:

36584c5345af3330795bdeec0b376c11.png

​ 在多线程的情况下,当多个线程执行了wait()方法后,需要其它线程执行notify()或者notifyAll()方法去唤醒,假如被阻塞的多个线程都被唤醒,但实际情况是被唤醒的线程中有一部分线程是不应该被唤醒的,那么对于这些不应该被唤醒的线程而言就是虚假唤醒。

问题复现

生产者与消费者问题

​ 假设当前有4个线程分别为A,B,C,D,其中A,B线程是生产者,C,D线程是消费者,当A和B线程生产了一个数据后就去通知消费者去消费,C和D消费掉这一个数据后就通知生产者去生产,数据的大小为1。也就是说正常情况下,数据只会有0和1两种状态,0表示生产者该生产数据了,1表示消费者该消费数据了。

package producer_consumer;

public class PVTest {

public static void main(String[] args) {

Data data = new Data();

//生产者线程A

new Thread(() -> {

for (int i = 0;i < 5;i++) {

try {

data.increment();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

},"A").start();

//生产者线程B

new Thread(() -> {

for (int i = 0;i < 5;i++) {

try {

data.increment();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

},"B").start();

//消费者线程C

new Thread(() -> {

for (int i = 0;i < 5;i++) {

try {

data.decrement();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

},"C").start();

//消费者线程D

new Thread(() -> {

for (int i = 0;i < 5;i++) {

try {

data.decrement();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

},"D").start();

}

}

//数据类

class Data {

//表示数据个数

private int number = 0;

public synchronized void increment() throws InterruptedException {

//关键点,这里应该使用while循环

if (number != 0) {

this.wait();

}

number++;

System.out.println(Thread.currentThread().getName()+"生产了数据:"+number);

this.notifyAll();

}

public synchronized void decrement() throws InterruptedException {

//关键点,这里应该使用while循环

if (number == 0) {

this.wait();

}

number--;

System.out.println(Thread.currentThread().getName()+"消费了数据:"+number);

this.notifyAll();

}

}

程序结果

033063b21b95ebe90b650794bf6369ba.png

结果分析

​ 可以看到上述结果出现了data为2的情况,不符合之前的预期,出现问题的场景是这样的:当data为1的时候,线程A和B先后获取锁去生产数据的时候会被阻塞住,然后消费者C或者D消费掉数据后去notifyAll()唤醒了线程A和B,被唤醒的A和B没有再次去判断data状态,就去执行后续增加数据的逻辑了,导致两个生产者都执行了increment(),最终data出现了2这种情况。也就是说线程A和B有一个是不应该被唤醒的却被唤醒了,出现这个问题的关键点在于程序中使用到了if判断,只判断了一次data的状态,应该使用while循环去判断

虚假唤醒问题解决

​ 正如JDK API文档中所说在写程序时候应该用while去替代if,上述生产者和消费者代码中,将Data类中的increment()和decrement()方法中的if换为while即可避免线程虚假唤醒的问题。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值