生产者消费者案例-虚假唤醒

正常的生产者消费者案例

我们测试的即是唤醒机制,如果没有等待和唤醒,则会出现生产了没有线程消费,消费完了,没有线程生产的现象,这种情况这里不进行验证,我们test wait()和notifyAll()的问题

//1 先定义一个售货员角色类
class Saler {
    private int product = 0;//产品数量
    public synchronized void get() { //进货
        if (product >= 5) { //5个为货满
            System.out.println("产品已满");
            try {
                this.wait();
            } catch (InterruptedException e) {
            }
        } else {
            System.out.println(Thread.currentThread().getName() + " : " + ++product);
            this.notifyAll();
        }
    }

    public synchronized void sale() { //卖货
        if (product <= 0) {
            System.out.println("缺货");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            System.out.println(Thread.currentThread().getName() + " : " + --product);
            this.notifyAll();
        }
    }
}
//2 定义1个生产者类
class Pro implements Runnable {
    private Saler saler;
    public Pro(Saler saler) {
        this.saler = saler;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            saler.get();
        }
    }
}
//3 定义1个消费者类
class Con implements Runnable {
    private Saler saler;

    public Con(Saler saler) {
        this.saler = saler;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            saler.sale();
        }
    }
}
//4 测试
public class Test {
    public static void main(String[] args) {
        Saler saler = new Saler();

        Pro pro = new Pro(saler);
        Con cus = new Con(saler);

        new Thread(pro, "生产者 A").start();
        new Thread(cus, "消费者 B").start();
    }
}
// 此时大概会有如下结果,我们到生产消费正常
生产者 A : 1
生产者 A : 2
生产者 A : 3
生产者 A : 4
生产者 A : 5
产品已满
消费者 B : 4
消费者 B : 3
消费者 B : 2
消费者 B : 1
消费者 B : 0
缺货
生产者 A : 1
生产者 A : 2
生产者 A : 3
生产者 A : 4
消费者 B : 3
消费者 B : 2
消费者 B : 1
消费者 B : 0
//如果把货满设置为1,则会出现交替的现象
生产者 A : 1
产品已满
消费者 B : 0
缺货
生产者 A : 1
产品已满
消费者 B : 0
缺货
生产者 A : 1
产品已满
消费者 B : 0
缺货
生产者 A : 1
产品已满
消费者 B : 0
缺货
生产者 A : 1
产品已满
消费者 B : 0
缺货

给生产者设置延时

run方法里添加200ms的延时,这时生产者会慢于消费者,货满仍然为1(为了明显显示效果)

class Pro implements Runnable {
    private Saler saler;

    public Pro(Saler saler) {
        this.saler = saler;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
            }

            saler.get();
        }
    }
}

public class Test {
    public static void main(String[] args) {
        Saler saler = new Saler();

        Pro pro = new Pro(saler);
        Con cus = new Con(saler);

        new Thread(pro, "生产者 A").start();
        new Thread(cus, "消费者 B").start();
    }
}
//结果为 可以看到最后一次生产的产品没有消费,且程序没有停止
缺货
生产者 A : 1
消费者 B : 0
缺货
生产者 A : 1
消费者 B : 0
缺货
生产者 A : 1
消费者 B : 0
缺货
生产者 A : 1
消费者 B : 0
缺货
生产者 A : 1
消费者 B : 0
生产者 A : 1
产品已满

//原因分析 如下,消费进的this.wait()后, 再次唤醒,由于else的存在,再次被唤醒后,则不会执行--product操作,
//消费者会提前结束循环,此时,生产者生的数据则不会继续被消费,故会一直处于wait状态
//为了解决这个问题,就该把else去掉
if (product <= 0) {
    System.out.println("缺货");
    try {
        this.wait();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
} else {
    System.out.println(Thread.currentThread().getName() + " : " + --product);
    this.notifyAll();
}
//则Saler如下
class Saler {
    private int product = 0;

    public synchronized void get() {//循环次数:0
        if (product >= 1) {
            System.out.println("产品已满");

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + " : " + ++product);
        this.notifyAll();
    }

    public synchronized void sale() {//product = 0; 循环次数:0
        if (product <= 0) {
            System.out.println("缺货");

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + " : " + --product);
        this.notifyAll();
    }
}

多个生产者消费者

能过以上的处理我们成功解决了上述问题
但当存在多个生产者消费者是,会出现新的问题,我们继续分析一下

public class Test {
    public static void main(String[] args) {
        Saler saler = new Saler();

        Pro pro = new Pro(saler);
        Con cus = new Con(saler);

        new Thread(pro, "生产者 A").start();
        new Thread(cus, "消费者 B").start();

        new Thread(pro, "生产者 C").start();
        new Thread(cus, "消费者 D").start();
    }
}

//结果如下 发现出现了负数
缺货
缺货
生产者 A : 1
消费者 B : 0
缺货
消费者 D : -1
缺货
生产者 C : 0
消费者 D : -1
缺货
消费者 B : -2
缺货
消费者 D : -3
缺货
消费者 B : -4
缺货
消费者 D : -5
缺货
消费者 B : -6
缺货
消费者 D : -7
缺货
消费者 B : -8
缺货
消费者 D : -9
缺货
消费者 B : -10
缺货
消费者 D : -11
缺货
消费者 B : -12
缺货
消费者 D : -13
缺货
消费者 B : -14
缺货
消费者 D : -15
缺货
消费者 B : -16
缺货
消费者 D : -17
消费者 B : -18
生产者 A : -17
生产者 C : -16
生产者 A : -15
生产者 C : -14
生产者 A : -13
生产者 C : -12
生产者 C : -11
生产者 A : -10
生产者 C : -9
生产者 A : -8
生产者 C : -7
生产者 A : -6
生产者 C : -5
生产者 A : -4
生产者 C : -3
生产者 A : -2
生产者 A : -1
生产者 C : 0

//原因分析 假设2个消费者同时抢占资源
//当1个消费者抢占锁后,发现product==0,则wait()并释放锁, 此时另一个消费者抢占资源,发现product==0,也wait()并释放锁
//此时当生产者执行notifyAll()后, 同时唤醒2个消费者,都执行--product操作,即产生负数情况
//这就叫做虚假唤醒
//解决方案,将if替换为while, 被唤醒后重新判断product值(jdk api中有说明)
class Saler {
    private int product = 0;

    public synchronized void get() {//循环次数:0
        while (product >= 1) {
            System.out.println("产品已满");

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + " : " + ++product);
        this.notifyAll();
    }

    public synchronized void sale() {//product = 0; 循环次数:0
        while (product <= 0) {
            System.out.println("缺货");

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + " : " + --product);
        this.notifyAll();
    }
}
//结果如下 即出现正确的交替结果
缺货
缺货
生产者 A : 1
消费者 B : 0
缺货
缺货
生产者 C : 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
缺货
缺货
生产者 A : 1
消费者 D : 0
缺货
缺货
生产者 C : 1
消费者 D : 0
缺货
缺货
生产者 A : 1
消费者 D : 0
缺货
缺货
生产者 C : 1
消费者 D : 0
缺货
缺货
生产者 A : 1
消费者 D : 0
缺货
缺货
生产者 C : 1
消费者 D : 0
缺货
缺货
生产者 A : 1
消费者 D : 0
缺货
生产者 C : 1
消费者 B : 0
缺货
缺货
生产者 A : 1
消费者 B : 0
缺货
缺货
生产者 C : 1
消费者 B : 0
缺货
生产者 A : 1
消费者 D : 0
缺货
生产者 C : 1
消费者 D : 0
Copyright © zhaojq 2019 all right reserved,powered by Gitbook本书发布时间: 2021-01-19 20:53:29

results matching ""

    No results matching ""