JAVA线程虚假唤醒

java

线程虚假唤醒问题描述

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

​ 在多线程的情况下,当多个线程执行了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();

}

}

程序结果

结果分析

​ 可以看到上述结果出现了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即可避免线程虚假唤醒的问题。

以上是 JAVA线程虚假唤醒 的全部内容, 来源链接: www.h5w3.com/python/703130.html

回到顶部