wait/notify实现生产者消费者模式

为什么要使用生产者和消费者模式?

在线程的世界中,生产者就是生产一些数据,而消费者就是把这些数据消费使用,但是他们的速度很可能就是不一致的,有的时候是生产者快有的时候生产者慢而消费者快,就需要有个设计模式去解决这个问题,而不至于一个过快一个过慢,于是就诞生了生产者消费者模式,这个设计模式实际上把生产方和消费方进行了解耦,从而达到更加流畅的配合。

能解决什么问题?

能解决生产过快消费不足或者生产不足消费过快的问题,而且能让生产方和消费方之间解耦。

代码演示

数据容器: 用于存储生产出的数据,定义了生产数据的put方法和消费数据的take方法。

  • put方法实现逻辑:首先两个方法都需要同步加锁,防止出现线程安全问题,而且wait也需要monitor锁,需要定义在synchronized修饰的方法里面。里面的逻辑也很简单,如果storage队列满了,我们就调用wait()等待,不再生产数据。
  • take方法实现逻辑:如果队列为空,就调用wait等待。
  • notify唤醒逻辑:
    • 生产者生产过快的情况:生产者生产过快,storage会处于满的状态,这时候不再生产数据处于等待状态,消费者那边会消费数据,消费完最后会调用notify方法唤醒生产者继续生产数据
    • 消费者消费过快的情况:消费者消费过快,storage会处于空的状态,这时候消费者这边检查到没有数据消费就处于等待状态,生产者那边生产出一条数据后会唤醒消费者继续消费数据
class EventStorage {

    private int maxSize;
    private LinkedList<Date> storage;

    public EventStorage() {
        maxSize = 10;
        storage = new LinkedList<>();
    }

    public synchronized void put() {
        while (storage.size() == maxSize) {
            try {
               wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        storage.add(new Date());
        System.out.println("仓库里有了" + storage.size() + "个产品。");
        notify();
    }

    public synchronized void take() {
        while (storage.size() == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("拿到了" + storage.poll() + ",现在仓库还剩下" + storage.size());
        notify();
    }
}

生产者: 用于生产数据,可以看到,生产者中storage用于存储生产出的数据,run方法用于完成生产数据的任务。

class Producer implements Runnable {

    private EventStorage storage;

    public Producer(EventStorage storage) {
        this.storage = storage;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            storage.put();
        }
    }
}

消费者: 用于消费数据

class Consumer implements Runnable {

    private EventStorage storage;

    public Consumer(EventStorage storage) {
        this.storage = storage;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            storage.take();
        }
    }
}

主类: 启动一个线程生产数据,另一个线程消费数据。

public class ProducerConsumerModel {

    public static void main(String[] args) {
        EventStorage eventStorage = new EventStorage();
        Producer producer = new Producer(eventStorage);
        Consumer consumer = new Consumer(eventStorage);
        new Thread(producer).start();
        new Thread(consumer).start();
    }
}

打印结果:

仓库里有了1个产品。
仓库里有了2个产品。
仓库里有了3个产品。
仓库里有了4个产品。
仓库里有了5个产品。
仓库里有了6个产品。
拿到了Sat Nov 02 20:33:09 CST 2019,现在仓库还剩下5
拿到了Sat Nov 02 20:33:09 CST 2019,现在仓库还剩下4
拿到了Sat Nov 02 20:33:09 CST 2019,现在仓库还剩下3
拿到了Sat Nov 02 20:33:09 CST 2019,现在仓库还剩下2
拿到了Sat Nov 02 20:33:09 CST 2019,现在仓库还剩下1
拿到了Sat Nov 02 20:33:09 CST 2019,现在仓库还剩下0
仓库里有了1个产品。
仓库里有了2个产品。
仓库里有了3个产品。
仓库里有了4个产品。
仓库里有了5个产品。
仓库里有了6个产品。
仓库里有了7个产品。
仓库里有了8个产品。
仓库里有了9个产品。
仓库里有了10个产品。
拿到了Sat Nov 02 20:33:09 CST 2019,现在仓库还剩下9
拿到了Sat Nov 02 20:33:09 CST 2019,现在仓库还剩下8
拿到了Sat Nov 02 20:33:09 CST 2019,现在仓库还剩下7
...

笔记来源:慕课网悟空老师视频《Java并发核心知识体系精讲》

发布了112 篇原创文章 · 获赞 303 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_36221788/article/details/102875419