zoukankan      html  css  js  c++  java
  • wait notify之虚假唤醒(一)

    虚假唤醒的概念

    jdk官方文档解释:

    所以说在wait和notify一块使用时,如果使用if作为条件时,会有虚假唤醒的情况发生,所以必须使用while作为循环条件。下面来举例实验:

    首先,创建一个资源类:(在多线程中,一般都是资源类和线程操作解耦,不放在用同一个类中,只有在线程操作资源类时,才会创建资源类的对象)

    package com.test;

    /**
    * 资源类
    * @author Huxudong
    * @createTime 2020-04-01 21:57:39
    **/
    public class Resource {
    /** 产品数 */
    private int product = 0;

    /** 进货 */
    public synchronized void get() {
    if(product >= 10) {
    System.out.println(Thread.currentThread().getName()+":"+"产品已满!");
    /** 当商品已经满的时候,进货线程挂起 */
    try {
    this.wait();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }

    /** 进货 */
    System.out.println(Thread.currentThread().getName()+":"+ ++product);
    /** 唤醒其他线程 */
    this.notifyAll();

    }

    /** 售货 */
    public synchronized void sale() {
    if(product <= 0) {
    System.out.println(Thread.currentThread().getName()+":"+"产品已空");
    try {
    this.wait();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }

    /** 售货 */
    System.out.println(Thread.currentThread().getName()+":"+ --product);
    /** 唤醒其他线程 */
    this.notify();
    }
    }

    然后再创建线程来操作我们的资源类(通过java8新特性Lambda表达式直接创建)

    package com.test;

    import java.util.concurrent.TimeUnit;

    /**
    * 线程操作资源类,实现线程与资源类的解耦合
    * @author Huxudong
    * @createTime 2020-04-01 23:13:54
    **/
    public class TestPc {
    public static void main(String[] args) {
    Resource resource = new Resource();
    new Thread(()->{
    for (int i = 0; i < 20; i++) {
    try {
    /** 睡眠,便于观察结果 */
    TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    resource.get();
    }
    },"生产者A").start();



    new Thread(()->{
    for (int i = 0; i < 20; i++) {
    resource.sale();
    }

    },"消费者C").start();

    new Thread(()->{
    for (int i = 0; i < 20; i++) {

    resource.get();
    }

    },"生产者B").start();

    new Thread(()->{
    for (int i = 0; i < 20; i++) {
    resource.sale();
    }

    },"消费者D").start();
    }
    }

    先来看看如果使用if条件会发生什么:
    
    

     对,你没看错,怎么可能会出现负数呢,这肯定是不对的。冷静下来分析一下,还是有点头绪,知道哪里出现了问题的(那你是一个处事不惊的人,很厉害)。

    来,分析一下,一开始先调用了消费者C,D线程(因为我们写了睡眠在生产者中),消费者此时发现此时product资源为0,所以,消费者C,D这两个兄弟,没办法只能调用wait方法,睡眠了,并且释放了锁。

    但是此时第一个消费者已经苏醒了,发动机开始生产产品了,并且生产之后,又唤醒了所有等待的消费者线程。这消费者C,D两兄弟终于苏醒了,D哥们先获得了锁,所以就先消费了一个产品,然后就又发现没有产品了,又伤心的休眠去了,但是不要忘了,此时还有一个C哥们被唤醒了啊,你唤醒了人家,人家总的干点什么事情吧,不然这多难受,刚好不巧的是,此时的判断条件是if,所以此时C哥们便不受条件的约束,接着上面自己睡眠的代码处执行,毅然决然的又去消费了一个产品,原来D哥们消费后,就已经为0了,这个C哥们再去消费减一,不就是-1了吗,以此类推分析。发现如果判断条件用不好,此时唤醒的C哥们就相当于虚假唤醒的了,会给程序带来不可预估的错误。所以在这里判断必须要使用while,先来看看把if换成while的结果。

     这回结果就比较正常了,为什么使用while就可以呢,因为像上文所说,即使唤醒了所有的消费者线程,此时会不停while循环判断,如果此时条件是为0,那么C哥们就不能出while,那么他也就不回执行下面消费产品的减减操作了,那么就会避免了这种错误。这也是官方提倡的在使用wait  和notifyAll的时候,必须使用while循环条件判断。

  • 相关阅读:
    pat00-自测5. Shuffling Machine (20)
    Spiral Matrix
    Search in Rotated Sorted Array II
    Search in Rotated Sorted Array
    Best Time to Buy and Sell Stock II
    4Sum
    3Sum Closest
    3Sum
    MySQL存储过程、函数和游标
    Word Ladder
  • 原文地址:https://www.cnblogs.com/clover-forever/p/12616869.html
Copyright © 2011-2022 走看看