Java多线程中wait()为什么要放在while循环中

首先来执行一段代码

开启两个线程,可以操作初始值为0的一个变量
实现一个线程对该变量加1,一个线程对该变量减1
实现交替,来10轮,变量初始值为0
public class ThreadWaitNotifyDemo {

    public static void main(String[] args) {

        AirConditioner airConditioner = new AirConditioner();

        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                airConditioner.increment();
            }
        } , "线程一").start();

        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                airConditioner.decrement();
            }
        } , "线程二").start();
    }

}

//共享资源类
class AirConditioner{
    private int number = 0;

    //加1
    public void increment(){
        synchronized (this) {
            //判断
            if (number != 0){
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //干活
            number++;
            System.out.println(Thread.currentThread().getName() + "加1之后为:" + number);
            //通知其他线程我的活干完了
            notifyAll();
        }
    }

    //减1
    public void decrement(){
        synchronized (this) {
            //判断
            if (number == 0){
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //干活
            number--;
            System.out.println(Thread.currentThread().getName() + "减1之后为:" + number);
            //通知其他线程我的活干完了
            notifyAll();
        }
    }
}

查看控制台输出结果

达到预期结果,实现两个线程交替操作

接着,我们添加两个线程,一共四个线程,其中两个线程做加法,两个线程做减法,四个线程交替执行,还来实现上面代码的过程,交替对数据加1或者减1,我们执行下面的代码

public class ThreadWaitNotifyDemo2 {

    public static void main(String[] args) {

        AirConditioner2 airConditioner2 = new AirConditioner2();

        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                airConditioner2.increment();
            }
        } , "线程一").start();

        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                airConditioner2.decrement();
            }
        } , "线程二").start();

        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                airConditioner2.increment();
            }
        } , "线程三").start();

        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                airConditioner2.decrement();
            }
        } , "线程四").start();
    }

}

//共享资源类
class AirConditioner2{
    private int number = 0;

    //加1
    public void increment(){
        synchronized (this) {
            if (number != 0){
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "加1之后为:" + number);
            notifyAll();
        }
    }

    //减1
    public void decrement(){
        synchronized (this) {
            if (number == 0){
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "减1之后为:" + number);
            notifyAll();
        }
    }
}

查看控制台输出结果

结果中共享数据出现了2和3,而我们预期的结果是对数据交替加减,交替输出0和1,此时就出现了问题

出现错误的原因就是多线程交互中,必须要防止多线程的虚假唤醒,也即(在判断中不能使用if,必须使用while)

我们此时看一下Java8的api文档

翻译一下,一个线程可以不需要notify来唤醒,可以被打断或超时,叫做虚假唤醒 虽然在实践中会很少发生,但是应用程序必须通过测试使线程唤醒的条件来预防这种现象 如果条件不满足,则继续等待。换句话说,等待应该总是循环发生

因此建议使用while循环来判断执行wait()方法的条件,那么为什么建议使用while循环呢?

就是用if判断的话,唤醒后线程会从wait结束之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait。

比如一个线程执行了wait之后,就会释放锁,假设还在if语句里面,其他线程抢到锁之后,会执行自己的操作,而共享数据可能会发生改变,而我们还在if语句里面,等到其他的线程执行notifyAll的时候,这个wait的线程就会被唤醒,然后代码会继续向下执行,但是,因为此时共享数据已经改变,而我们已经进入到了if语句里面了,可能按照现在共享数据的状态我们根本不能进入if语句中,但实际上已经进去了,因此就会发生冲突,代码会向下执行,对数据做出修改。如果改为while循环来判断的话,即使这个wait的线程被唤醒之后,如果想要执行判断语句之外的代码(也就是跳出while循环),还需要通过while循环再次判断一下条件是否满足,满足就继续wait,不满足才会向下执行,因此就需要用while来控制是否执行wait()方法

在此之后我们将上述的代码中判断语句改为使用while判断

然后执行下面的代码

public class ThreadWaitNotifyDemo2 {

    public static void main(String[] args) {

        AirConditioner2 airConditioner2 = new AirConditioner2();

        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                airConditioner2.increment();
            }
        } , "线程一").start();

        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                airConditioner2.decrement();
            }
        } , "线程二").start();

        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                airConditioner2.increment();
            }
        } , "线程三").start();

        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                airConditioner2.decrement();
            }
        } , "线程四").start();
    }

}

//共享资源类
class AirConditioner2{
    private int number = 0;

    //加1
    public void increment(){
        synchronized (this) {
            //if (number != 0){
            while (number != 0){
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "加1之后为:" + number);
            notifyAll();
        }
    }

    //减1
    public void decrement(){
        synchronized (this) {
            //if (number == 0){
            while (number == 0){
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "减1之后为:" + number);
            notifyAll();
        }
    }
}

查看控制台输出结果

达到预期结果,实现四个线程交替动作

这就是为什么Java使用多线程时wait()要放在while()循环中,其实最大的区别就是if只会执行一次,执行完会接着向下执行if()外边的;而while不会,直到条件满足才会向下执行while()外边的

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值