java,消费者-生产者模式

265 阅读3分钟

什么是消费者-生产者模式

简单来说,就是存在两个线程,公用一块共享区域,但是需要特别说明的是:

  1. 当共享区域满时,生产者线程必须等待(阻塞);
  2. 当共享区域空时,消费者线程必须等待(阻塞)。

比如当我们去饭店吃饭的时候,这个时候厨师为生产者,客人为消费者。当没有客人的时候,厨师需要等待有客人才能做菜;当客满的时候,客人必须等待。

实现方式

Object的wait/notify消息通知

  • wait:将当前线程置为休眠状态,知道接到通知或被中断;
  • notify:从waiting状态的线程中,挑选一个线程进行通知(唤醒),将这个线程从等待队列移入同步队列。
  • notifyall:唤醒所有waiting线程,并移入到同步队列中。
class Test2{
        LinkedList<Integer> linkedList = new LinkedList<>();
        int size = 10;
        ExecutorService executorService = Executors.newFixedThreadPool(15);
        for(int i = 0; i < 5; i++){
            executorService.submit(new Product(linkedList, size));
        }
        for(int i = 0; i < 10; i++){
            executorService.submit(new Customer(linkedList));
        }
        executorService.shutdown();


    }

    static class Product extends Thread{
        private LinkedList<Integer> linkedList;
        private int size;
        Product(LinkedList<Integer> linkedList, int size){
            this.linkedList = linkedList;
            this.size = size;
        }

        @Override
        public void run() {
            while (true){
                synchronized (linkedList){
                    try {
                        while(linkedList.size() == 9){
                            System.out.println(Thread.currentThread().getName() + "共享队列已满, 生产者开始等待......");
                            linkedList.wait();
                            System.out.println(Thread.currentThread().getName() + ", 生产者等待完毕,开始生产");
                        }
                        int idx = new Random().nextInt();
                        System.out.println(Thread.currentThread().getName() + "产品id:" + idx);
                        linkedList.add(idx);
                        linkedList.notifyAll();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    static class Customer extends Thread{
        private LinkedList<Integer> linkedList;
        Customer(LinkedList<Integer> linkedList){
            this.linkedList = linkedList;
        }

        @Override
        public void run() {
            while (true){
                synchronized (linkedList){
                    try {
                        while (linkedList.isEmpty()){
                            System.out.println(Thread.currentThread().getName() + "共享队列为空, 消费者开始等待......");
                            linkedList.wait();
                            System.out.println(Thread.currentThread().getName() + "消费者开始消费......");
                        }
                        int tmp = linkedList.removeLast();
                        System.out.println(Thread.currentThread().getName() + "消费者账单id:" + tmp);
                        linkedList.notifyAll();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        }
    }

Lock的condition的await/signal

与object用法类似,不同的地方在于使用condition之前需要取得锁

class Test3{
    static ReentrantLock lock = new ReentrantLock();
    static Condition full = lock.newCondition();
    static Condition empty = lock.newCondition();
    public static void main(String[] args) {
        LinkedList<Integer> linkedList = new LinkedList<>();
        ExecutorService executorService = Executors.newFixedThreadPool(15);
        for(int i = 0; i < 8; i++){
            executorService.submit(new Product(linkedList, 10, lock));
        }

        for(int i = 0; i < 12; i++){
            executorService.submit(new Customer(linkedList, lock));
        }
        executorService.shutdown();

    }

    static class Product implements Runnable {
        LinkedList<Integer> linkedList;
        int size;
        Lock lock;
        Product(LinkedList<Integer> linkedList, int size, Lock lock){
            this.linkedList = linkedList;
            this.size = size;
            this.lock = lock;
        }


        @Override
        public void run() {
            while (true){
                lock.lock();
                try {
                    while (linkedList.size() == size){
                        System.out.println(Thread.currentThread().getName() + "共享队列已满,生产者开始等待...");
                        full.await();
                        System.out.println(Thread.currentThread().getName() + "生产者等待完毕...");
                    }
                    int idx = new Random().nextInt();
                    linkedList.add(idx);
                    System.out.println(Thread.currentThread().getName() + "生产产品id:" + idx);
                    empty.signalAll();

                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
            }
        }
    }

    static class Customer implements Runnable{
        LinkedList<Integer> linkedList;
        int size;
        Lock lock;
        Customer(LinkedList<Integer> linkedList, Lock lock){
            this.linkedList = linkedList;
            this.lock = lock;
        }
        @Override
        public void run() {
            while (true){
                lock.lock();
                try {
                    while (linkedList.isEmpty()){
                        System.out.println(Thread.currentThread().getName() + "共享队列为空,消费开始等待...");
                        empty.await();
                        System.out.println(Thread.currentThread().getName() + "生消费者等待完毕...");
                    }
                    int idx = linkedList.removeLast();
                    System.out.println(Thread.currentThread().getName() + "消费产品id:" + idx);
                    full.signalAll();
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
            }
        }
    }
}

基于BlockingQueue

BlockingQueue 只不过是在put和pop方法里面添加了基于condition的锁

class Test4{
    static LinkedBlockingQueue<Integer> linkedBlockingDeque = new LinkedBlockingQueue<>();
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(15);
        for(int i = 0; i < 8; i++){
            executorService.submit(new Product(linkedBlockingDeque));
        }

        for(int i = 0; i < 12; i++){
            executorService.submit(new Customer(linkedBlockingDeque));
        }
        executorService.shutdown();
    }
    static class Product implements Runnable{
        LinkedBlockingQueue<Integer> blockingQueue;
        Product(LinkedBlockingQueue<Integer> blockingQueue){
            this.blockingQueue = blockingQueue;
        }

        @Override
        public void run() {
            while (true){
                try {
                    int idx = new Random().nextInt();
                    blockingQueue.put(idx);
                    System.out.println(Thread.currentThread().getName() + "生产产品id:" + idx);
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

    static class Customer implements Runnable{
        LinkedBlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<>();
        Customer(LinkedBlockingQueue<Integer> blockingQueue){
            this.blockingQueue = blockingQueue;
        }
        @Override
        public void run() {
            while (true){
                try {
                    int idx = blockingQueue.take();
                    System.out.println(Thread.currentThread().getName() + "消费产品id:" + idx);
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

refrence

同步队列与等待队列的运行机制

  1. 每个Condition对象都包含一个等待队列
  2. 在Object的监视器模型上,一个对象拥有一个同步队列与一个等待队列,而AQS拥有一个同步队列和多个等待队列。
  • 调用condition的await方法,将会使当前线程进入等待队列并释放锁(先加入等待队列再释放锁),同时线程状态转为等待状态。
  • 调用condition的signal方法时,将会把等待队列的首节点移到等待队列的尾部,然后唤醒该节点。被唤醒,并不代表就会从await方法返回,也不代表该节点的线程能获取到锁,它一样需要加入到锁的竞争acquireQueued方法中去,只有成功竞争到锁,才能从await方法返回。