生产者消费者 2023-02-15 20:59 ### 介绍 生产者消费者是并发操作中常见的问题,或者说一种模式、模型。一个线程负责生产数据,一个线程负责消费数据,两个线程同时去操作一个缓冲区。当缓冲区满了的时候,生产者不能再生产,只能等待;当缓冲区空了的时候,消费者不能再消费,只能等待。缓冲区是临界资源,线程必须互斥的访问。 这样做的好处是生产者只管生产数据,消费者只管消费数据,业务解耦。 > 以下代码:https://github.com/hanhanhanxu/ProConDemo - 1、wait、notifyAll实现。 - 2、wait、notifyAll实现的另一种写法。 - 3、LinkedBlockingDeque实现。 - 4、SynchronousQueue实现。 ### 一、wait、notifyAll实现: ```java package com.example.procondemo; import lombok.extern.slf4j.Slf4j; import java.util.LinkedList; import java.util.List; /** * wait notifyAll 实现生产者消费者 * lock Condition的实现差不多 */ @Slf4j public class ProducerConsumer1 { public static void main(String[] args) { // 缓冲区最多能放多少元素 int MAX_COUNT = 5; // 缓冲区,这里不必使用线程安全的集合,因为对集合的操作都是在锁内 List<Object> list = new LinkedList<>(); // 共用一把锁 Object monitor = new Object(); new Thread(new Producer(monitor, MAX_COUNT, list)).start(); new Thread(new Producer(monitor, MAX_COUNT, list)).start(); new Thread(new Consumer(monitor, list)).start(); new Thread(new Consumer(monitor, list)).start(); } } @Slf4j class Producer implements Runnable { private Object monitor; private int maxCount; private List list; public Producer(Object monitor, int maxCount, List list) { this.monitor = monitor; this.maxCount = maxCount; this.list = list; } @Override public void run() { int i = 0; while (i < 10) { i++; synchronized (monitor) { // 每次抢到锁后先检查是否能生产,不能生产就wait while (list.size() >= maxCount) { try { monitor.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 生产 Object o = new Object(); list.add(o); log.info("生产者:{} 生产了数据:{}", Thread.currentThread().getName(), o); // 生产完成后,唤醒其他线程 monitor.notifyAll(); } } } } @Slf4j class Consumer implements Runnable { private Object monitor; private List list; public Consumer(Object monitor, List list) { this.monitor = monitor; this.list = list; } @Override public void run() { int i = 0; while (i < 10) { i++; synchronized (monitor) { // 抢到锁后先检查是否能消费,不能消费就wait while (list.size() <= 0) { try { monitor.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 消费 Object o = list.remove(list.size() - 1); log.info("消费者:{} 消费了数据:{}", Thread.currentThread().getName(), o); // 消费完成后,唤醒其他线程 monitor.notifyAll(); } } } } /** * 0、最外层while(true)的作用 * 1、为什么要用while 而不是if * 2、为什么要用notifyAll 而不是notify */ ``` #### 运行结果 ``` E:\JDK\JDK8\bin\java.exe ... 20:25:05.694 [Thread-0] INFO com.example.procondemo.Producer - 生产者:Thread-0 生产了数据:java.lang.Object@6b24e9a4 20:25:05.702 [Thread-0] INFO com.example.procondemo.Producer - 生产者:Thread-0 生产了数据:java.lang.Object@5ca3a7b0 20:25:05.702 [Thread-0] INFO com.example.procondemo.Producer - 生产者:Thread-0 生产了数据:java.lang.Object@c362537 20:25:05.702 [Thread-0] INFO com.example.procondemo.Producer - 生产者:Thread-0 生产了数据:java.lang.Object@10426f78 20:25:05.702 [Thread-0] INFO com.example.procondemo.Producer - 生产者:Thread-0 生产了数据:java.lang.Object@5e51561f 20:25:05.702 [Thread-3] INFO com.example.procondemo.Consumer - 消费者:Thread-3 消费了数据:java.lang.Object@5e51561f 20:25:05.702 [Thread-3] INFO com.example.procondemo.Consumer - 消费者:Thread-3 消费了数据:java.lang.Object@10426f78 20:25:05.702 [Thread-3] INFO com.example.procondemo.Consumer - 消费者:Thread-3 消费了数据:java.lang.Object@c362537 20:25:05.702 [Thread-3] INFO com.example.procondemo.Consumer - 消费者:Thread-3 消费了数据:java.lang.Object@5ca3a7b0 20:25:05.702 [Thread-3] INFO com.example.procondemo.Consumer - 消费者:Thread-3 消费了数据:java.lang.Object@6b24e9a4 20:25:05.702 [Thread-1] INFO com.example.procondemo.Producer - 生产者:Thread-1 生产了数据:java.lang.Object@17a5f293 20:25:05.702 [Thread-1] INFO com.example.procondemo.Producer - 生产者:Thread-1 生产了数据:java.lang.Object@19620e17 20:25:05.702 [Thread-1] INFO com.example.procondemo.Producer - 生产者:Thread-1 生产了数据:java.lang.Object@1d5d99e3 20:25:05.702 [Thread-1] INFO com.example.procondemo.Producer - 生产者:Thread-1 生产了数据:java.lang.Object@585f042c 20:25:05.702 [Thread-1] INFO com.example.procondemo.Producer - 生产者:Thread-1 生产了数据:java.lang.Object@605644de 20:25:05.702 [Thread-2] INFO com.example.procondemo.Consumer - 消费者:Thread-2 消费了数据:java.lang.Object@605644de 20:25:05.702 [Thread-2] INFO com.example.procondemo.Consumer - 消费者:Thread-2 消费了数据:java.lang.Object@585f042c 20:25:05.702 [Thread-2] INFO com.example.procondemo.Consumer - 消费者:Thread-2 消费了数据:java.lang.Object@1d5d99e3 20:25:05.702 [Thread-2] INFO com.example.procondemo.Consumer - 消费者:Thread-2 消费了数据:java.lang.Object@19620e17 20:25:05.702 [Thread-2] INFO com.example.procondemo.Consumer - 消费者:Thread-2 消费了数据:java.lang.Object@17a5f293 20:25:05.702 [Thread-0] INFO com.example.procondemo.Producer - 生产者:Thread-0 生产了数据:java.lang.Object@1f0ef442 20:25:05.702 [Thread-0] INFO com.example.procondemo.Producer - 生产者:Thread-0 生产了数据:java.lang.Object@22ac7509 20:25:05.702 [Thread-0] INFO com.example.procondemo.Producer - 生产者:Thread-0 生产了数据:java.lang.Object@2116f78b 20:25:05.702 [Thread-0] INFO com.example.procondemo.Producer - 生产者:Thread-0 生产了数据:java.lang.Object@75a3bc18 20:25:05.702 [Thread-0] INFO com.example.procondemo.Producer - 生产者:Thread-0 生产了数据:java.lang.Object@1d67fa0a 20:25:05.702 [Thread-3] INFO com.example.procondemo.Consumer - 消费者:Thread-3 消费了数据:java.lang.Object@1d67fa0a 20:25:05.702 [Thread-3] INFO com.example.procondemo.Consumer - 消费者:Thread-3 消费了数据:java.lang.Object@75a3bc18 20:25:05.702 [Thread-3] INFO com.example.procondemo.Consumer - 消费者:Thread-3 消费了数据:java.lang.Object@2116f78b 20:25:05.703 [Thread-3] INFO com.example.procondemo.Consumer - 消费者:Thread-3 消费了数据:java.lang.Object@22ac7509 20:25:05.703 [Thread-3] INFO com.example.procondemo.Consumer - 消费者:Thread-3 消费了数据:java.lang.Object@1f0ef442 20:25:05.703 [Thread-1] INFO com.example.procondemo.Producer - 生产者:Thread-1 生产了数据:java.lang.Object@69cdfc75 20:25:05.703 [Thread-1] INFO com.example.procondemo.Producer - 生产者:Thread-1 生产了数据:java.lang.Object@182ee22e 20:25:05.703 [Thread-1] INFO com.example.procondemo.Producer - 生产者:Thread-1 生产了数据:java.lang.Object@3870fb63 20:25:05.703 [Thread-1] INFO com.example.procondemo.Producer - 生产者:Thread-1 生产了数据:java.lang.Object@6a523807 20:25:05.703 [Thread-1] INFO com.example.procondemo.Producer - 生产者:Thread-1 生产了数据:java.lang.Object@6c7a4f6c 20:25:05.703 [Thread-2] INFO com.example.procondemo.Consumer - 消费者:Thread-2 消费了数据:java.lang.Object@6c7a4f6c 20:25:05.703 [Thread-2] INFO com.example.procondemo.Consumer - 消费者:Thread-2 消费了数据:java.lang.Object@6a523807 20:25:05.703 [Thread-2] INFO com.example.procondemo.Consumer - 消费者:Thread-2 消费了数据:java.lang.Object@3870fb63 20:25:05.703 [Thread-2] INFO com.example.procondemo.Consumer - 消费者:Thread-2 消费了数据:java.lang.Object@182ee22e 20:25:05.703 [Thread-2] INFO com.example.procondemo.Consumer - 消费者:Thread-2 消费了数据:java.lang.Object@69cdfc75 Process finished with exit code 0 ``` #### 0、最外层while(i < 10)的作用 控制生产者/消费者进行生产/消费的次数,避免一直运行下去,方便观察。 #### 1、为什么要用while 而不是if 如果使用if,则有可能发送线程先进入if的wait部分,等待;然后被唤醒后,没有检测条件是否满足list.size() >= maxCount或list.size() <= 0就进行生产/消费,这样就会出错。 【多线程编程时,涉及条件检查时,大部分都应该使用while而不是if。因为线程争抢资源的能力是不确定的,有可能被唤醒的时候资源还没好,或已经被他人消费导致不满足条件了,此时应该继续等待】 #### 2、为什么要用notifyAll 而不是notify 如果使用notify,多生产者/消费者场景下,可能会导致死锁的发生。( https://www.bilibili.com/video/BV1p44y1L7uU/ 6:40) 【多线程编程时,大部分场景都是使用notifyAll,除非深刻理解了当前场景能使用notify】 ### 二、另一种写法: 基于wait notifyAll通常还有另一种写法: ```java package com.example.procondemo; import lombok.extern.slf4j.Slf4j; import java.util.LinkedList; import java.util.List; /** * @author: HanXu * on 2022/2/15 * Class description: 生产者消费者 * 基于wait notifyAll的另一种写法 */ public class ProducerConsumer3 { public static void main(String[] args) { Channel channel = new Channel(5); new Thread(new ProducerRunnable(channel)).start(); new Thread(new ProducerRunnable(channel)).start(); new Thread(new ConsumerRunnable(channel)).start(); new Thread(new ConsumerRunnable(channel)).start(); } } class Channel { private List list = new LinkedList(); private int capacity; public Channel(int capacity) { this.capacity = capacity; } public synchronized void put(Object o) { while (list.size() >= capacity) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } list.add(o); notifyAll(); } public synchronized Object get() { while (list.size() <= 0) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } Object o = list.remove(list.size() - 1); return o; } } @Slf4j class ProducerRunnable implements Runnable { private Channel channel; public ProducerRunnable(Channel channel) { this.channel = channel; } @Override public void run() { for (int i = 0; i < 10; i++) { Object o = new Object(); channel.put(o); log.info("生产者:{} 生产了数据:{}", Thread.currentThread().getName(), o); } } } @Slf4j class ConsumerRunnable implements Runnable { private Channel channel; public ConsumerRunnable(Channel channel) { this.channel = channel; } @Override public void run() { for (int i = 0; i < 10; i++) { Object o = channel.get(); log.info("消费者:{} 消费了一个数据:{}", Thread.currentThread().getName(), o); } } } ``` #### 运行结果 ``` E:\JDK\JDK8\bin\java.exe ... 20:55:51.916 [Thread-3] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-3 消费了一个数据:java.lang.Object@4db9fa88 20:55:51.916 [Thread-1] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-1 生产了数据:java.lang.Object@11cd5821 20:55:51.916 [Thread-2] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-2 消费了一个数据:java.lang.Object@11cd5821 20:55:51.916 [Thread-0] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-0 生产了数据:java.lang.Object@4db9fa88 20:55:51.925 [Thread-0] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-0 生产了数据:java.lang.Object@4ecd7cda 20:55:51.925 [Thread-3] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-3 消费了一个数据:java.lang.Object@1b353351 20:55:51.925 [Thread-1] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-1 生产了数据:java.lang.Object@1b353351 20:55:51.925 [Thread-0] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-0 生产了数据:java.lang.Object@2cb6c6cc 20:55:51.925 [Thread-2] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-2 消费了一个数据:java.lang.Object@4ecd7cda 20:55:51.925 [Thread-3] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-3 消费了一个数据:java.lang.Object@2cb6c6cc 20:55:51.925 [Thread-1] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-1 生产了数据:java.lang.Object@5e37ae53 20:55:51.925 [Thread-3] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-3 消费了一个数据:java.lang.Object@5e37ae53 20:55:51.925 [Thread-2] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-2 消费了一个数据:java.lang.Object@6a1ec33d 20:55:51.925 [Thread-0] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-0 生产了数据:java.lang.Object@6a1ec33d 20:55:51.925 [Thread-3] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-3 消费了一个数据:java.lang.Object@4d6c1ee7 20:55:51.925 [Thread-1] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-1 生产了数据:java.lang.Object@4d6c1ee7 20:55:51.925 [Thread-3] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-3 消费了一个数据:java.lang.Object@43abe93a 20:55:51.925 [Thread-0] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-0 生产了数据:java.lang.Object@43abe93a 20:55:51.925 [Thread-0] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-0 生产了数据:java.lang.Object@410d833f 20:55:51.925 [Thread-2] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-2 消费了一个数据:java.lang.Object@2540e9e4 20:55:51.925 [Thread-0] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-0 生产了数据:java.lang.Object@64031c68 20:55:51.925 [Thread-3] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-3 消费了一个数据:java.lang.Object@410d833f 20:55:51.925 [Thread-1] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-1 生产了数据:java.lang.Object@2540e9e4 20:55:51.925 [Thread-2] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-2 消费了一个数据:java.lang.Object@64031c68 20:55:51.925 [Thread-0] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-0 生产了数据:java.lang.Object@7e992682 20:55:51.925 [Thread-3] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-3 消费了一个数据:java.lang.Object@7e992682 20:55:51.925 [Thread-1] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-1 生产了数据:java.lang.Object@6fa330c0 20:55:51.925 [Thread-0] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-0 生产了数据:java.lang.Object@62821547 20:55:51.925 [Thread-3] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-3 消费了一个数据:java.lang.Object@62821547 20:55:51.925 [Thread-1] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-1 生产了数据:java.lang.Object@5d3e29de 20:55:51.925 [Thread-0] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-0 生产了数据:java.lang.Object@4f3b6653 20:55:51.925 [Thread-3] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-3 消费了一个数据:java.lang.Object@4f3b6653 20:55:51.925 [Thread-1] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-1 生产了数据:java.lang.Object@3e18511c 20:55:51.925 [Thread-2] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-2 消费了一个数据:java.lang.Object@6fa330c0 20:55:51.925 [Thread-1] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-1 生产了数据:java.lang.Object@1cb590f2 20:55:51.925 [Thread-2] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-2 消费了一个数据:java.lang.Object@1cb590f2 20:55:51.925 [Thread-1] INFO com.example.procondemo.ProducerRunnable - 生产者:Thread-1 生产了数据:java.lang.Object@6ae69694 20:55:51.925 [Thread-2] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-2 消费了一个数据:java.lang.Object@6ae69694 20:55:51.925 [Thread-2] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-2 消费了一个数据:java.lang.Object@3e18511c 20:55:51.925 [Thread-2] INFO com.example.procondemo.ConsumerRunnable - 消费者:Thread-2 消费了一个数据:java.lang.Object@5d3e29de Process finished with exit code 0 ``` ### 三、阻塞队列实现: LinkedBlockingDeque阻塞队列实现 ```java package com.example.procondemo; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.LinkedBlockingDeque; /** * 阻塞队列实现生产者消费者 * 阻塞队列自带阻塞属性,所以实现起来比较简单 * 添加元素 取出元素 * 阻塞 put take * 超时 offer(timeout) poll(timeout) * * 返回特定值 offer poll * 抛出异常 add remove */ @Slf4j public class ProducerConsumer2 { private static final LinkedBlockingDeque queue = new LinkedBlockingDeque(10); static class ProducerThread extends Thread { @Override public void run() { int i = 0; while (i < 10) { i++; try { Object o = new Object(); queue.put(o); log.info("生产者:{} 生产了一个数据:{}", Thread.currentThread().getName(), o); } catch (InterruptedException e) { e.printStackTrace(); } } } } static class ConsumerThread extends Thread { @Override public void run() { int i = 0; while (i < 10) { i++; try { Object take = queue.take(); log.info("消费者:{} 消费了一个数据:{}", Thread.currentThread().getName(), take); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { new ProducerThread().start(); new ConsumerThread().start(); new ProducerThread().start(); new ConsumerThread().start(); } } ``` #### 运行结果 ``` E:\JDK\JDK8\bin\java.exe ... 20:31:32.030 [Thread-0] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-0 生产了一个数据:java.lang.Object@7a9b8fef 20:31:32.036 [Thread-1] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-1 消费了一个数据:java.lang.Object@7a9b8fef 20:31:32.038 [Thread-0] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-0 生产了一个数据:java.lang.Object@4ee16d30 20:31:32.038 [Thread-0] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-0 生产了一个数据:java.lang.Object@4f92bba3 20:31:32.038 [Thread-0] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-0 生产了一个数据:java.lang.Object@35ece832 20:31:32.038 [Thread-0] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-0 生产了一个数据:java.lang.Object@6cb28e4a 20:31:32.038 [Thread-0] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-0 生产了一个数据:java.lang.Object@29fff601 20:31:32.038 [Thread-0] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-0 生产了一个数据:java.lang.Object@736a1f3e 20:31:32.038 [Thread-0] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-0 生产了一个数据:java.lang.Object@2115d419 20:31:32.036 [Thread-2] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-2 生产了一个数据:java.lang.Object@b2a7223 20:31:32.038 [Thread-1] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-1 消费了一个数据:java.lang.Object@4ee16d30 20:31:32.036 [Thread-3] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-3 消费了一个数据:java.lang.Object@b2a7223 20:31:32.038 [Thread-1] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-1 消费了一个数据:java.lang.Object@4f92bba3 20:31:32.038 [Thread-0] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-0 生产了一个数据:java.lang.Object@59adceaf 20:31:32.038 [Thread-2] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-2 生产了一个数据:java.lang.Object@1d21953 20:31:32.038 [Thread-1] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-1 消费了一个数据:java.lang.Object@6cb28e4a 20:31:32.038 [Thread-1] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-1 消费了一个数据:java.lang.Object@29fff601 20:31:32.038 [Thread-3] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-3 消费了一个数据:java.lang.Object@35ece832 20:31:32.038 [Thread-0] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-0 生产了一个数据:java.lang.Object@f347465 20:31:32.038 [Thread-2] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-2 生产了一个数据:java.lang.Object@44d0870d 20:31:32.038 [Thread-1] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-1 消费了一个数据:java.lang.Object@736a1f3e 20:31:32.038 [Thread-3] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-3 消费了一个数据:java.lang.Object@2115d419 20:31:32.038 [Thread-2] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-2 生产了一个数据:java.lang.Object@1c334ede 20:31:32.038 [Thread-1] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-1 消费了一个数据:java.lang.Object@59adceaf 20:31:32.038 [Thread-3] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-3 消费了一个数据:java.lang.Object@1d21953 20:31:32.038 [Thread-2] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-2 生产了一个数据:java.lang.Object@755ee453 20:31:32.038 [Thread-1] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-1 消费了一个数据:java.lang.Object@f347465 20:31:32.038 [Thread-3] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-3 消费了一个数据:java.lang.Object@44d0870d 20:31:32.038 [Thread-3] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-3 消费了一个数据:java.lang.Object@755ee453 20:31:32.038 [Thread-1] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-1 消费了一个数据:java.lang.Object@1c334ede 20:31:32.038 [Thread-2] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-2 生产了一个数据:java.lang.Object@6457732a 20:31:32.038 [Thread-3] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-3 消费了一个数据:java.lang.Object@6457732a 20:31:32.038 [Thread-2] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-2 生产了一个数据:java.lang.Object@24ff7842 20:31:32.038 [Thread-3] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-3 消费了一个数据:java.lang.Object@24ff7842 20:31:32.038 [Thread-3] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-3 消费了一个数据:java.lang.Object@2586be33 20:31:32.038 [Thread-2] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-2 生产了一个数据:java.lang.Object@2586be33 20:31:32.038 [Thread-2] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-2 生产了一个数据:java.lang.Object@204283c5 20:31:32.038 [Thread-1] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-1 消费了一个数据:java.lang.Object@204283c5 20:31:32.038 [Thread-2] INFO com.example.procondemo.ProducerConsumer2 - 生产者:Thread-2 生产了一个数据:java.lang.Object@7f537ac4 20:31:32.038 [Thread-3] INFO com.example.procondemo.ProducerConsumer2 - 消费者:Thread-3 消费了一个数据:java.lang.Object@7f537ac4 Process finished with exit code 0 ``` #### 分析 阻塞队列由于put():当队列满时阻塞,和take():当队列空时阻塞,自带阻塞属性,所以生产者和消费者都比较简单,直接放、取元素就行。 ### 四、另一种阻塞队列: SynchronousQueue阻塞队列实现 ```java package com.example.procondemo; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.Executors; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * @Author:Hanxu * @url:https://riun.xyz/ * @Date:2023/2/16 21:34 * 使用 SynchronousQueue 实现生产者消费者 */ @Slf4j public class ProducerConsumer4 { public static void main(String[] args) { SynchronousQueue<Object> synchronousQueue = new SynchronousQueue<>(); Runnable runnable = () -> { int i = 0; while (i < 10) { i++; Object o = new Object(); log.info("生产者:{} 生产了一个数据:{}", Thread.currentThread().getName(), o); try { synchronousQueue.put(o); } catch (InterruptedException e) { e.printStackTrace(); } } }; Runnable runnable1 = () -> { int i = 0; while (i < 10) { i++; Object o = null; try { o = synchronousQueue.take(); } catch (InterruptedException e) { e.printStackTrace(); } log.info("消费者:{} 消费了一个数据:{}", Thread.currentThread().getName(), o); } }; ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(6); executorService.submit(runnable); executorService.submit(runnable); executorService.submit(runnable); executorService.submit(runnable1); executorService.submit(runnable1); executorService.submit(runnable1); try { // 等待子线程完成任务 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } log.info("线程池已完成任务数:" + executorService.getCompletedTaskCount()); while (executorService.getCompletedTaskCount() != 6) { Thread.yield(); } executorService.shutdown(); for (int i = 0; i < 10; i++) { try { boolean b = executorService.awaitTermination(2, TimeUnit.SECONDS); if (b) { break; } } catch (InterruptedException e) { e.printStackTrace(); } } log.info("线程池已终结:" + executorService.isTerminated()); } } ``` 上述代码输出: ``` E:\JDK\JDK8\bin\java.exe ... 21:57:13.587 [pool-1-thread-1] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-1 生产了一个数据:java.lang.Object@6ba57a75 21:57:13.587 [pool-1-thread-2] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-2 生产了一个数据:java.lang.Object@6b4a4bc8 21:57:13.587 [pool-1-thread-3] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-3 生产了一个数据:java.lang.Object@570912f6 21:57:13.590 [pool-1-thread-2] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-2 生产了一个数据:java.lang.Object@3eae895c 21:57:13.590 [pool-1-thread-5] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-5 消费了一个数据:java.lang.Object@6ba57a75 21:57:13.590 [pool-1-thread-3] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-3 生产了一个数据:java.lang.Object@e2a3b68 21:57:13.590 [pool-1-thread-6] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-6 消费了一个数据:java.lang.Object@6b4a4bc8 21:57:13.590 [pool-1-thread-1] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-1 生产了一个数据:java.lang.Object@1f43873f 21:57:13.590 [pool-1-thread-4] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-4 消费了一个数据:java.lang.Object@570912f6 21:57:13.590 [pool-1-thread-2] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-2 生产了一个数据:java.lang.Object@c449b48 21:57:13.590 [pool-1-thread-6] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-6 消费了一个数据:java.lang.Object@e2a3b68 21:57:13.590 [pool-1-thread-5] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-5 消费了一个数据:java.lang.Object@3eae895c 21:57:13.590 [pool-1-thread-3] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-3 生产了一个数据:java.lang.Object@19d4e47c 21:57:13.590 [pool-1-thread-2] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-2 生产了一个数据:java.lang.Object@49e35680 21:57:13.590 [pool-1-thread-4] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-4 消费了一个数据:java.lang.Object@1f43873f 21:57:13.590 [pool-1-thread-3] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-3 生产了一个数据:java.lang.Object@38228f50 21:57:13.590 [pool-1-thread-1] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-1 生产了一个数据:java.lang.Object@5ff52070 21:57:13.590 [pool-1-thread-5] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-5 消费了一个数据:java.lang.Object@19d4e47c 21:57:13.590 [pool-1-thread-4] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-4 消费了一个数据:java.lang.Object@49e35680 21:57:13.590 [pool-1-thread-6] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-6 消费了一个数据:java.lang.Object@c449b48 21:57:13.590 [pool-1-thread-2] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-2 生产了一个数据:java.lang.Object@704e407f 21:57:13.590 [pool-1-thread-4] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-4 消费了一个数据:java.lang.Object@38228f50 21:57:13.590 [pool-1-thread-5] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-5 消费了一个数据:java.lang.Object@5ff52070 21:57:13.590 [pool-1-thread-1] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-1 生产了一个数据:java.lang.Object@4e88162a 21:57:13.590 [pool-1-thread-3] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-3 生产了一个数据:java.lang.Object@56c7e48 21:57:13.590 [pool-1-thread-2] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-2 生产了一个数据:java.lang.Object@3ae0d343 21:57:13.590 [pool-1-thread-6] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-6 消费了一个数据:java.lang.Object@704e407f 21:57:13.590 [pool-1-thread-5] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-5 消费了一个数据:java.lang.Object@4e88162a 21:57:13.590 [pool-1-thread-1] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-1 生产了一个数据:java.lang.Object@3108180e 21:57:13.590 [pool-1-thread-4] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-4 消费了一个数据:java.lang.Object@56c7e48 21:57:13.590 [pool-1-thread-3] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-3 生产了一个数据:java.lang.Object@5ac2f4f6 21:57:13.590 [pool-1-thread-6] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-6 消费了一个数据:java.lang.Object@3ae0d343 21:57:13.590 [pool-1-thread-5] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-5 消费了一个数据:java.lang.Object@3108180e 21:57:13.590 [pool-1-thread-1] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-1 生产了一个数据:java.lang.Object@32710b6f 21:57:13.590 [pool-1-thread-4] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-4 消费了一个数据:java.lang.Object@5ac2f4f6 21:57:13.590 [pool-1-thread-3] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-3 生产了一个数据:java.lang.Object@648b0cd6 21:57:13.590 [pool-1-thread-2] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-2 生产了一个数据:java.lang.Object@12d6818c 21:57:13.590 [pool-1-thread-5] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-5 消费了一个数据:java.lang.Object@32710b6f 21:57:13.590 [pool-1-thread-1] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-1 生产了一个数据:java.lang.Object@44f14bf1 21:57:13.590 [pool-1-thread-4] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-4 消费了一个数据:java.lang.Object@648b0cd6 21:57:13.590 [pool-1-thread-3] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-3 生产了一个数据:java.lang.Object@48ff4718 21:57:13.590 [pool-1-thread-2] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-2 生产了一个数据:java.lang.Object@41d28200 21:57:13.590 [pool-1-thread-5] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-5 消费了一个数据:java.lang.Object@44f14bf1 21:57:13.590 [pool-1-thread-1] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-1 生产了一个数据:java.lang.Object@4ac62c03 21:57:13.590 [pool-1-thread-6] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-6 消费了一个数据:java.lang.Object@12d6818c 21:57:13.590 [pool-1-thread-3] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-3 生产了一个数据:java.lang.Object@2e07fe71 21:57:13.590 [pool-1-thread-4] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-4 消费了一个数据:java.lang.Object@48ff4718 21:57:13.590 [pool-1-thread-2] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-2 生产了一个数据:java.lang.Object@431d7ffd 21:57:13.590 [pool-1-thread-5] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-5 消费了一个数据:java.lang.Object@41d28200 21:57:13.590 [pool-1-thread-6] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-6 消费了一个数据:java.lang.Object@4ac62c03 21:57:13.590 [pool-1-thread-1] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-1 生产了一个数据:java.lang.Object@3bff953d 21:57:13.590 [pool-1-thread-2] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-2 生产了一个数据:java.lang.Object@7fdbd616 21:57:13.590 [pool-1-thread-4] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-4 消费了一个数据:java.lang.Object@2e07fe71 21:57:13.590 [pool-1-thread-1] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-1 生产了一个数据:java.lang.Object@1efcb5e 21:57:13.590 [pool-1-thread-6] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-6 消费了一个数据:java.lang.Object@3bff953d 21:57:13.590 [pool-1-thread-3] INFO com.example.procondemo.ProducerConsumer4 - 生产者:pool-1-thread-3 生产了一个数据:java.lang.Object@632fca03 21:57:13.590 [pool-1-thread-5] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-5 消费了一个数据:java.lang.Object@431d7ffd 21:57:13.590 [pool-1-thread-4] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-4 消费了一个数据:java.lang.Object@7fdbd616 21:57:13.590 [pool-1-thread-6] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-6 消费了一个数据:java.lang.Object@1efcb5e 21:57:13.590 [pool-1-thread-6] INFO com.example.procondemo.ProducerConsumer4 - 消费者:pool-1-thread-6 消费了一个数据:java.lang.Object@632fca03 21:57:14.601 [main] INFO com.example.procondemo.ProducerConsumer4 - 线程池已完成任务数:6 21:57:14.601 [main] INFO com.example.procondemo.ProducerConsumer4 - 线程池已终结:true Process finished with exit code 0 ``` --END--
发表评论