Java并发编程——wait()虚假唤醒

博客讨论了Java中多线程的生产者消费者问题,指出在使用wait()方法时可能出现的虚假唤醒问题,导致数据不一致。通过分析错误的运行结果,发现使用if判断条件不足,应当改为while循环确保条件检查。修复后的代码展示了如何正确地处理等待和通知,确保数据状态的正确性。修正后的程序运行结果符合预期,避免了数据异常。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在生产者和消费者问题中,使用wait()方法可能造成的虚假唤醒和解决方法

问题代码:

public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}
class Data{//判断等待
    private int number=0;
    //+1
    public synchronized void increment() throws InterruptedException {
        if (number!=0){
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"->"+number);
        //通知其他线程,我+1完毕了
        this.notifyAll();
    }

    //-1
    public synchronized void decrement() throws InterruptedException {
        if (number==0){
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"->"+number);
        //通知其他线程,我-1完毕了
        this.notifyAll();
    }
}

错误运行结果
A->1
B->0
A->1
B->0
A->1
B->0
A->1
B->0
A->1
B->0
A->1
B->0
C->1
A->2
C->3
B->2
B->1
B->0
C->1
A->2
C->3
B->2
D->1
D->0
C->1
A->2
C->3
D->2
D->1
C->2
A->3
C->4
D->3
D->2
D->1
D->0
C->1
D->0
C->1
D->0
出现了不是0或1的数据。
原因:
判断number!=0和number==0时,使用了if进行判断,造成了虚假唤醒。
解决方法:
阅读JDK1.8的帮助文档
Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0).
The current thread must own this object’s monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object’s monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.
As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:
synchronized (obj) {
while ()
obj.wait();
… // Perform action appropriate to condition
}
翻译内容:
请添加图片描述
所以不能使用if进行判断,应该使用while循环进行判断
正确代码:

public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}
class Data{//判断等待
    private int number=0;
    //+1
    public synchronized void increment() throws InterruptedException {
        while (number!=0){
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"->"+number);
        //通知其他线程,我+1完毕了
        this.notifyAll();
    }

    //-1
    public synchronized void decrement() throws InterruptedException {
        while (number==0){
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"->"+number);
        //通知其他线程,我-1完毕了
        this.notifyAll();
    }
}

正确运行结果:
A->1
B->0
A->1
B->0
A->1
B->0
A->1
B->0
C->1
B->0
A->1
B->0
C->1
B->0
A->1
B->0
C->1
B->0
A->1
D->0
C->1
B->0
A->1
D->0
C->1
D->0
A->1
D->0
C->1
D->0
A->1
D->0
C->1
D->0
C->1
D->0
C->1
D->0
C->1
D->0
问题解决!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alchemy_Ding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值