深入浅出多线程(七)之生产者消费者模式

423 阅读3分钟

这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战

前言

面试的老生常谈就是设计模式,从设计模式可以延伸到各种实战业务,但一般我们都会问几个常用的,单例、工厂这些模式,把话题引申到我们擅长的领域,当被问到单例的时候,我们可以说我们在业务中常用的生产者消费者模式~

生产者消费者模式

生产消费者模式并不是GOF提出的23种设计模式之一,23种设计模式是建立在面向对象的基础上,但其实面向过程的编程中也有很多高效的设计模式,生产者消费者模式便是其中之一,它是我们编程中最常用的一种设计模式。
在实际的软件开发过程中,经常会碰到如下场景:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类、函数、线程、进程等)。产生数据的模块,就形象地称为生产者;而处理数据的模块,就称为消费者。
单单抽象出生产者和消费者,还够不上是生产者/消费者模式。该模式还需要有一个缓冲区处于生产者和消费者之间,作为一个中介。生产者把数据放入缓冲区,而消费者从缓冲区取出数据。

实现(使用wait()方法和notify()方法实现生产者消费者模式)

  • 缓冲区(队列形式):
    • 参数:
      • maxSize:队列容量的最大程度
      • storage:定义一个LinkedList去实现队列装载、
    • 方法:
      • put():判断缓冲区容量是否最大值,如果是则调用wait()方法,陷入阻塞;反之,插入队列,并唤醒线程;
      • take():判断缓冲区容量是否为0,如果是则调用wait()方法,陷入阻塞;反之,弹出队列,并唤醒线程
  • 生产者:
    • 构造缓冲区函数
    • 继承Runnable接口并重写Run方法,在里面遍历调用put()
  • 消费者:
    • 构造缓冲区函数
    • 继承Runnable接口并重写Run方法,在里面遍历调用take()
  • 主线程Main()方法:
    • 构造缓冲区、生产者、消费者方法
    • 构造两个线程并传入生产者、消费者。
    • 启动两个线程
public class ProducerConsumerModel {
    public static void main(String[] args) {
        EventStorage eventStorage = new EventStorage();
        Producer producer = new Producer(eventStorage);
        Consumer consumer = new Consumer(eventStorage);
        Thread thread = new Thread(producer);
        Thread thread1 = new Thread(consumer);
        thread.start();
        thread1.start();
    }
}

//生产者
class Producer implements Runnable{

    private EventStorage eventStorage;

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

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

//消费者
class Consumer implements Runnable{

    private EventStorage eventStorage;

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

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

//定义一个队列
class EventStorage{
    private int maxSize;
    private LinkedList<Date> storage;

    public EventStorage() {
        this.maxSize = 10;
        this.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();
            }
        }
        storage.poll();
        System.out.println("已取出,当前容量为"+storage.size());
        notify();
    }
}

总结

在实际的业务中,信息服务器要接收大批量的客户端请求,原来那种串行化的处理,根本无法及时处理客户端请求,造成信息服务器大量请求堆积,导致丢包异常严重。之后就采用了生产者消费者模式,在业务请求与业务处理间,建立了一个List 类型的缓冲区,服务端接收到业务请求,就往里扔,然后再去接收下一个业务请求,而 多个业务处理线程,就会去缓冲区里取业务请求处理。这样就大大提高了服务器的相应速度。