Java线程虚假唤醒


#什么是虚假唤醒
#为什么会出现虚假唤醒
#解决的方法

什么是虚假唤醒

如图,JavaJdk10API文档这样描述

来源于JavaJdk10API文档
意思就是说线程可以在没有被通知,中断或者超时的情况下被唤醒,这就是所谓的虚假唤醒

为什么会出现虚假唤醒

1.当Object.wait()这个方法运行时,当前的线程会进入等待状态,并自动释放锁。当被其他线程唤醒时,它会在wait()之后的地方继续开始运行
2.当Object.notifyAll运行时,会唤醒所有处于等待状态的线程同时进行抢夺锁,而导致两个线程会同时进行重写抢锁。
下面举个例子

package testJUC;
/**
 * 测试java线程的虚假唤醒
 */
public class TestFalseAwaken {
    public static void main(String[] args) {
        Product product = new Product();
        //创建2个线程运行
        new Thread(() -> {
            for (int i = 0; i < 20; i++)
                new Producer2(product).getProduct();
        }, "生产者A").start();
        new Thread(() -> {
            for (int i = 0; i < 20; i++)
                new Consumer2(product).saleProduct();
        }, "消费者A").start();
        new Thread(() -> {
            for (int i = 0; i < 20; i++)
                new Producer2(product).getProduct();
        }, "生产者B").start();
        new Thread(() -> {
           for (int i = 0; i < 20; i++)
                new Consumer2(product).saleProduct();
        }, "消费者B").start();
    }

}

/**
 * 产品
 * 为了放大Java线程的虚假唤醒和方便测试结果,设置库存的容量为1
 */
class Product {
    //库存
    private int count = 0;

    //生产鞋子
    public synchronized void getProduct() {
        if (count != 0) {
            try {
                System.out.println("库存已经满,正在通知消费者消费");
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + "生产了一个产品,剩余的库存->" + (++count));
        this.notifyAll();
    }

    //卖出鞋子
    public synchronized void saleProduct() {
        if (count == 0) {
            try {
                System.out.println("库存不够,正在通知生产者生产");
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + "买了一个产品,剩余的库存->" + (--count));
        this.notifyAll();
    }
}

/**
 * 生产者
 */
class Producer2 {
    private Product product;

    public Producer2(Product product) {
        this.product = product;
    }
    //生产鞋子
    public void getProduct() {
        product.getProduct();
    }
}

/**
 * 消费者
 */
class Consumer2 {
    private Product product;

    public Consumer2(Product product) {
        this.product = product;
    }
    //卖鞋子
    public void saleProduct() {
        product.saleProduct();
    }
}

上述代码使用if判断,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码。
运行的结果可能出现下列情况
来源于本人电脑运行的结果
为什么会出现这种情况呢?
1.首先有四个线程: 生产者A,消费者A,生产者A,消费者B。
2.当多个生产者线程同时进入生产产品的方法时,而判断条件if只进行一次判断,就会出现的count值多次加1,导致出现这种结果。

解决的方法

来源于Javajdk10API文档
意思就是说推荐的方法是以等待是检查被期待已久的条件放在一个While里面,循环到wait。简单的来说,就是把if改成while。
以本文所提供的代码为例,把if的判断改成while就可以有效解决此问题,改善的代码如下

package testJUC;
/**
 * 测试java线程的虚假唤醒
 */
public class TestFalseAwaken {
    public static void main(String[] args) {
        Product product = new Product();
        //创建2个线程运行
        new Thread(() -> {
            for (int i = 0; i < 20; i++)
                new Producer2(product).getProduct();
        }, "生产者A").start();
        new Thread(() -> {
            for (int i = 0; i < 20; i++)
                new Consumer2(product).saleProduct();
        }, "消费者A").start();
        new Thread(() -> {
            for (int i = 0; i < 20; i++)
                new Producer2(product).getProduct();
        }, "生产者B").start();
        new Thread(() -> {
            for (int i = 0; i < 20; i++)
                new Consumer2(product).saleProduct();
        }, "消费者B").start();
    }

}

/**
 * 产品
 * 为了放大Java线程的虚假唤醒和方便测试结果,设置库存的容量为1
 */
class Product {
    //库存
    private int count = 0;

    //生产鞋子
    public synchronized void getProduct() {
        while (count != 0) {
            try {
                System.out.println("库存已经满,正在通知消费者消费");
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + "生产了一个产品,剩余的库存->" + (++count));
        this.notifyAll();
    }

    //卖出鞋子
    public synchronized void saleProduct() {
        while (count == 0) {
            try {
                System.out.println("库存不够,正在通知生产者生产");
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + "买了一个产品,剩余的库存->" + (--count));
        this.notifyAll();
    }
}

/**
 * 生产者
 */
class Producer2 {
    private Product product;

    public Producer2(Product product) {
        this.product = product;
    }
    //生产鞋子
    public void getProduct() {
        product.getProduct();
    }
}

/**
 * 消费者
 */
class Consumer2 {
    private Product product;

    public Consumer2(Product product) {
        this.product = product;
    }
    //卖鞋子
    public void saleProduct() {
        product.saleProduct();
    }
}

运行的结果如下
来源于本人电脑运行的结果

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值