使用notifyAll完善多线程下的生产者消费者模型

使用notifyAll完善多线程下的生产者消费者模型

我们先看一下wait()的代码注释:

使当前线程等待,直到另一个线程调用了这个对象的notify()或notifyAll()方法,换句话说,这个方法是执行了wait(0);

当前线程必须拥有该对象的监视器。线程释放此监视器的所有权,并等待另一个线程通过调用notify()或notifyAll()方法,让这个对象监视器上等待的线程被唤醒,然后线程等待,直到它可以重新获得监视器的所有权并恢复执行。

notify()是通知一个线程唤醒,而notifyAll()是会将wait()在同一个monitor的所有线程都会唤醒,而我们之前出现问题就是因为notify是随机的,那么我们现在使用notifyAll试一下:

/**
 * @program: ThreadDemo
 * @description: 优化多线程下的生产者消费者模型,避免多生产者-多消费者造成的程序假死
 * @author: hs96.cn@Gmail.com
 * @create: 2020-09-07
 */
public class ProduceConsumerVersion3 {
    private int i = 0;

    private final Object LOCK = new Object();

    private volatile boolean isProduced = false;

    public void produce() {
        synchronized (LOCK) {
            if (isProduced) {
                try {
                    LOCK.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + "->" + (++i));
            LOCK.notifyAll();
            isProduced = true;
        }
    }

    public void consume() {
        synchronized (LOCK) {
            if (!isProduced) {
                try {
                    LOCK.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + "->" + i);
            LOCK.notifyAll();
            isProduced = false;
        }
    }

    public static void main(String[] args) {
        ProduceConsumerVersion3 pc = new ProduceConsumerVersion3();
        Stream.of("P1", "P2", "P3").forEach(n -> new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    pc.produce();
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, n).start());

        Stream.of("C1", "C2", "C3", "C4").forEach(n -> new Thread(() -> {
            while (true) {
                pc.consume();
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, n).start());
    }
}

运行效果如下:

可以看到有连续生产2个的情况,那么是什么原因导致的呢?

其实仔细想一下,就是p3的生产完之后释放锁,然后p2抢到了,就继续生产了,那么怎么解决呢?让p2wait到p3生产的数据消费掉即可了,改进代码如下:‘

/**
 * @program: ThreadDemo
 * @description: 优化多线程下的生产者消费者模型,避免多生产者-多消费者造成的程序假死
 * @author: hs96.cn@Gmail.com
 * @create: 2020-09-07
 */
public class ProduceConsumerVersion3 {
    private int i = 0;

    private final Object LOCK = new Object();

    private volatile boolean isProduced = false;

    public void produce() {
        synchronized (LOCK) {
            while (isProduced) {
                try {
                    //System.out.println(Thread.currentThread().getName()+"wait....");
                    LOCK.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + "->" + (++i));
            LOCK.notifyAll();
            isProduced = true;
        }
    }

    public void consume() {
        synchronized (LOCK) {
            while (!isProduced) {
                try {
                    //System.out.println(Thread.currentThread().getName()+"wait....");
                    LOCK.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + "->" + i);
            LOCK.notifyAll();
            isProduced = false;
        }
    }

    public static void main(String[] args) {
        ProduceConsumerVersion3 pc = new ProduceConsumerVersion3();
        Stream.of("P1", "P2", "P3").forEach(n -> new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    pc.produce();
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, n).start());

        Stream.of("C1", "C2", "C3", "C4").forEach(n -> new Thread(() -> {
            while (true) {
                pc.consume();
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, n).start());
    }
}

运行效果如下:

posted @ 2020-09-07 01:03  风暴松鼠  阅读(127)  评论(0)    收藏  举报