手写生产者消费者模式

334 阅读2分钟

前置条件

  • 生产者生产数据,消费者消费数据,所以需要一个公共的池子来支持,生产者生产了数据放入池子中,消费者从这个池子里消费,我们用队列来维护这个池子。
  • 生产者生产完之后需要告知消费者,池子里的数据被消费完,消费者需要等待生产者生产,所以我们需要一个等待通知机制来处理这块逻辑 -- 代码中采用condition 搭配lock来处理。
  • 池子有其大小容量,所以需要个变量来维护这个容量。

定义变量

// 容量
private int capacity = 10;
// 队列
private Queue<Integer> queue = new ArrayBlockingQueue<>(capacity);
private ReentrantLock lock = new ReentrantLock();
private Condition add = lock.newCondition();
private Condition remove = lock.newCondition();

简单生产者

class Producer extends Thread{

    @Override
    public void run() {
        produce();
    }

    private void produce(){
        lock.lock();
        try {
            while (true) {
                if (queue.size() == capacity) {
                    try {
                        System.out.println("生产者进入等待");
                        add.await();
                        System.out.println("生产者不等了");
                    } catch (InterruptedException e) {
                    }
                }

                queue.offer(1);
                remove.signal();
                Thread.sleep(1000);
                System.out.println("生产者生产 1 条,队列长度:" + queue.size());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}

简单消费者

class Consumer extends Thread{

    @Override
    public void run() {
        consume();
    }
    
    private void consume(){
        lock.lock();
        try {
            while (true) {
                if (queue.isEmpty()) {
                    try {
                        System.out.println("消费者进入等待");
                        remove.await();
                        System.out.println("消费者不等了");
                    } catch (InterruptedException e) {
                    }
                }
                Integer some = queue.poll();
                add.signal();
                Thread.sleep(1000);
                System.out.println("消费者消费 1 条:" + some + " 队列剩余:" + queue.size() + "个元素");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}

输出

image.png

一个问题

为什么这里生产者生产完了消费者才消费,消费者消费完了生产者才生产呢?

是因为生产者消费者共用同一个lock,await()方法触发了才会释放锁,代码中await在isEmpty()和size == capacity的条件判断中,所以才会发生这种情况

解决方案

private ReentrantLock lock1 = new ReentrantLock();
private ReentrantLock lock2 = new ReentrantLock();
private Condition add = lock1.newCondition();
private Condition remove = lock2.newCondition();

我们用两个lock,condition分别对应其中一个即可解决

输出

image.png 当然了,生产者执行到打印语句的时候,消费者早都消费完了,所以打印队列长度:0

注意点

lock 需要搭配try-finally进行解锁,condition作用域在lock-unlock之间,类似于wait-notify作用在synchronized块内