生产者消费者模式与阻塞队列
生产者消费者模式与阻塞队列
在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。
Why 生产者消费者模式
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这种生产消费能力不均衡的问题,所以便有了生产者和消费者模式。
What 生产者消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
线程池与生产者消费者模式
Java中的线程池类其实就是一种生产者和消费者模式的实现方式,其实现方式更加高明。生产者把任务丢给线程池,线程池创建线程并处理任务,如果将要运行的任务数大于线程池的基本线程数就把任务扔到阻塞队列里,这种做法比只使用一个阻塞队列来实现生产者和消费者模式显然要高明很多,因为消费者能够处理直接就处理掉了,这样速度更快,而生产者先存,消费者再取这种方式显然慢一些。
我们的系统也可以使用线程池来实现多生产者消费者模式。比如创建N个不同规模的Java线程池来处理不同性质的任务,比如线程池1将数据读到内存之后,交给线程池2里的线程继续处理压缩数据。线程池1主要处理IO密集型任务,线程池2主要处理CPU密集型任务。
阻塞队列
阻塞队列是一个支持两个附件操作的队列,这两个附加的操作支持阻塞的插入和移除方法。
阻塞队列常用于生产者,消费着的场景,生产者是向队列里添加元素的线程,消费者是从队列里取元素的线程,阻塞队列就是生产者用来存放元素,消费者用来获取元素的容器。
JDK中提供了7个阻塞队列,分别是
- ArrayBlockingQueue 一个由数组结构组成的有界阻塞队列
数组实现的有界阻塞队列,按照先进先出原则对元素进行排序。默认不保证线程公平。
- LinkedBlockingQueue 一个由链表结构组成的有界阻塞队列
链表实现的有界阻塞队列,按照先进先出原则对元素进行排序
- priorityBlockingQueue 一个支持优先级排序的无界阻塞队列
支持优先级的无界阻塞队列,可以自定义实现CompareTo()方法来指定元素排序规则
- DelayQueue 一个使用优先级队列实现的无界阻塞队列
支持延时获取元素的无界阻塞队列,即当队列中的元素到达延迟时间时才会被取出。
- SynchronousQueue 一个不存储元素的阻塞队列
不存储元素的阻塞队列,每一个put操作必须等待一个take操作。否则不能继续添加元素。可以看成是一个传球手,负责把生产者线程处理的数据直接传递给消费者线程,队列本身并不存储任何元素,非常适合传递性场景。
- LinkedTransferQueue 一个由链表结构组成的无界阻塞队列
是一个由链表组成的无界阻塞队列,相对于其他阻塞队列,多了trytransfer 方法和trasnfer方法。
tansfer方法:如果有消费者正在等待接收元素,transfer可以把生产者传入的元素立刻传输给消费者,如果没有消费者在等待接收元素,transfer方法会将元素存放在队列的tail节点,并等到该元素被消费者消费了才返回。
- LinkedBlockingDueue 一个由链表结构组成的双向阻塞队列
双向队列指的是可以从队列的两端插入和移除元素。
Author: corn1ng
Link: https://corn1ng.github.io/2018/01/19/生产者消费者模式与阻塞队列/
License: 知识共享署名-非商业性使用 4.0 国际许可协议