首页 > 其他 > 详细

生产者消费者模式----虚假唤醒

时间:2021-06-03 23:52:35      阅读:27      评论:0      收藏:0      [点我收藏+]

JAVA 多线程 线程通信

生产者消费者模式----虚假唤醒

首先来看一组代码

代码来源于java多线程基础总结【四】虚假唤醒

package com.zsjt.test;

public class Test1 {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();

        Producer producer = new Producer(clerk);
        Consumer consumer = new Consumer(clerk);

        new Thread(producer, "生产者1").start();
        new Thread(consumer, "消费者1").start();
    }
}

class Clerk {//店员类
    private static final int TOTAL = 1;//数字取1是为了放大问题
    private int num = 0;

    public synchronized void get() {//店员买货
        if (num >= TOTAL) {
            System.out.println(Thread.currentThread().getName() + " : "+"库存已满,无法进货");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
            System.out.println(Thread.currentThread().getName() + " : " + (++num));
            this.notifyAll();

    }

    public synchronized void sale() {//店员卖货
        if (num <= 0) {
            System.out.println(Thread.currentThread().getName() + " : "+"库存已空,无法卖货");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
            System.out.println(Thread.currentThread().getName() + " : " + (num--));
            this.notifyAll();

    }
}

class Producer implements Runnable {
    private Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(200);//放大问题
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.get();
        }
    }
}

class Consumer implements Runnable {
    private Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            clerk.sale();
        }
    }
}

本例中,生产者负责进货,消费者负责卖货,运行结果如下所示

技术分享图片

可以看到,消费者在num==0时,提示无库存,并进入等待,待生产者进货后唤醒消费者,消费者再进行卖货,这是没有问题的,但是当我们再加入一组生产者和消费者呢,我们直接在main方法中加入,代码如下

package com.zsjt.test;


public class Test1 {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();

        Producer producer = new Producer(clerk);
        Consumer consumer = new Consumer(clerk);

        new Thread(producer, "生产者1").start();
        new Thread(consumer, "消费者1").start();
        new Thread(producer,"生产者2").start();
        new Thread(consumer,"消费者2").start();
    }
}

下面是有多个生产者或者消费者运行结果

技术分享图片

可以看到 数据中出现负数,此时就出现问题,这就是虚假唤醒,那么问题出现的原因是什么呢?

我们来看一下代码逻辑,首先消费者执行,此时num==0 消费者打印出库存不足,两个消费者都进入阻塞状态,然后生产者进货,当生产者线程进货后,此时将两个消费者唤醒,消费者会直接执行wait()方法后的代码,即num--,因为两个消费者都执行了此代码,则将num变为负数,这就是出现错误的原因。jdk 帮助文档提供的解决方式就是将wait 方法放在while循环中,将if替换为while,这样就会在线程被唤醒后,会继续执行判断该线程是否等待的逻辑,即能保证数据安全,使用if的情况下两个消费者线程都会直接执行wait后的代码

下面是替换后的代码

package com.zsjt.test;


public class Test1 {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();

        Producer producer = new Producer(clerk);
        Consumer consumer = new Consumer(clerk);

        new Thread(producer, "生产者1").start();
        new Thread(consumer, "消费者1").start();
        new Thread(producer,"生产者2").start();
        new Thread(consumer,"消费者2").start();
    }
}

class Clerk {//店员类
    private static final int TOTAL = 1;//数字取1是为了放大问题
    private int num = 0;

    public synchronized void get() {//店员买货
        while (num >= TOTAL) {
            System.out.println(Thread.currentThread().getName() + " : "+"库存已满,无法进货");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
            System.out.println(Thread.currentThread().getName() + " : " + (++num));
            this.notifyAll();

    }

    public synchronized void sale() {//店员卖货
        while (num <= 0) {
            System.out.println(Thread.currentThread().getName() + " : "+"库存已空,无法卖货");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
            System.out.println(Thread.currentThread().getName() + " : " + (num--));
            this.notifyAll();

    }
}

class Producer implements Runnable {
    private Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(200);//放大问题
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.get();
        }
    }
}

class Consumer implements Runnable {
    private Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            clerk.sale();
        }
    }
}

技术分享图片

技术分享图片

此时数据正常

生产者消费者模式----虚假唤醒

原文:https://www.cnblogs.com/zhaoshiqimr/p/14846392.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!