导言
最近一个月一向在更新《解读Java源码专栏》,其中跟咱们一同分析了Java的常见的5种BlockingQueue(堵塞行列),今日就盘点一下这几种堵塞行列的优缺点、差异,以及运用场景。
常见的BlockingQueue
有以下5种,下面会详细介绍。
- ArrayBlockingQueue
根据数组完成的堵塞行列,创立行列时需指定容量巨细,是有界行列。
- LinkedBlockingQueue
根据链表完成的堵塞行列,默认是无界行列,创立能够指定容量巨细
- SynchronousQueue
一种没有缓冲的堵塞行列,生产出的数据需要立刻被消费
- PriorityBlockingQueue
完成了优先级的堵塞行列,能够依照元素巨细排序,是无界行列
- DelayQueue
完成了推迟功用的堵塞行列,根据PriorityQueue完成的,是无界行列
BlockingQueue简介
这几种堵塞行列都是完成了BlockingQueue接口,在日常开发中,咱们如同很少用到BlockingQueue(堵塞行列)
,BlockingQueue
到底有什么效果?运用场景是什么样的?
假如运用过线程池或者阅览过线程池源码,就会知道线程池的核心功用都是根据BlockingQueue
完成的。
咱们用过消息行列(MessageQueue),就知道消息行列效果是解耦、异步、削峰。相同BlockingQueue
的效果也是这三种,差异是BlockingQueue
只效果于本机器,而消息行列相当于分布式BlockingQueue
。
BlockingQueue
作为堵塞行列,主要运用于生产者-顾客形式的场景,在并发多线程中尤其常用。
- 比如像线程池中的使命调度场景,提交使命和拉取并履行使命。
- 生产者与顾客解耦的场景,生产者把数据放到行列中,顾客从行列中取数据进行消费。两者进行解耦,不用感知对方的存在。
- 应对突发流量的场景,事务高峰期忽然来了很多请求,能够放到行列中缓存起来,顾客以正常的频率从行列中拉取并消费数据,起到削峰的效果。
BlockingQueue
是个接口,界说了几组放数据和取数据的办法,来满意不同的场景。
操作 | 抛出反常 | 回来特定值 | 堵塞 | 堵塞一段时刻 |
---|---|---|---|---|
放数据 | add() | offer() | put() | offer(e, time, unit) |
取数据(一同删去数据) | remove() | poll() | take() | poll(time, unit) |
取数据(不删去) | element() | peek() | 不支持 | 不支持 |
这四组办法的差异是:
- 当行列满的时分,再次添加数据,add()会抛出反常,offer()会回来false,put()会一向堵塞,offer(e, time, unit)会堵塞指定时刻,然后回来false。
- 当行列为空的时分,再次取数据,remove()会抛出反常,poll()会回来null,take()会一向堵塞,poll(time, unit)会堵塞指定时刻,然后回来null。
ArrayBlockingQueue
-
ArrayBlockingQueue
底层根据数组完成,选用循环数组,提升了数组的空间利用率。 -
ArrayBlockingQueue
初始化的时分,有必要指定行列长度,是有界的堵塞行列,所以要预估好行列长度,确保生产者和顾客速率相匹配。 -
ArrayBlockingQueue
的办法是线程安全的,运用ReentrantLock
在操作前后加锁来确保线程安全。
更多概况点击:ArrayBlockingQueue源码分析
LinkedBlockingQueue
-
LinkedBlockingQueue
底层根据链表完成,支持从头部弹出数据,从尾部添加数据。 -
LinkedBlockingQueue
初始化的时分,假如不指定行列长度,默认长度是Integer最大值,相当于无界行列,有内存溢出危险,主张初始化的时分指定行列长度。 -
LinkedBlockingQueue
的办法是线程安全的,别离运用了读写两把锁,比ArrayBlockingQueue
功能更好。
与ArrayBlockingQueue
差异是:
- 底层结构不同,
ArrayBlockingQueue
底层根据数组完成,初始化的时分有必要指定数组长度,无法扩容。LinkedBlockingQueue
底层根据链表完成,链表最大长度是Integer最大值。 - 占用内存巨细不同,
ArrayBlockingQueue
一旦初始化,数组长度就确认了,不会跟着元素添加而改动。LinkedBlockingQueue
会跟着元素越多,链表越长,占用内存越大。 - 功能不同,
ArrayBlockingQueue
的入队和出队共用一把锁,并发较低。LinkedBlockingQueue
入队和出队运用两把独立的锁,并发情况下功能更高。 - 公正锁选项,
ArrayBlockingQueue
初始化的时分,能够指定运用公正锁或者非公正锁,公正锁形式下,能够依照线程等候的顺序来操作行列。LinkedBlockingQueue
只支持非公正锁。 - 适用场景不同,
ArrayBlockingQueue
适用于明确限制行列巨细的场景,避免生产速度大于消费速度的时分,造成内存溢出、资源耗尽。LinkedBlockingQueue
适用于事务高峰期能够主动扩展消费速度的场景。
更多概况点击:LinkedBlockingQueue源码分析
SynchronousQueue
无论是ArrayBlockingQueue
还是LinkedBlockingQueue
都是起到缓冲行列的效果,当顾客的消费速度跟不上时,使命就在行列中堆积,需要等候顾客渐渐消费。
假如咱们想要自己的使命快速履行,不要积压在行列中,该怎样办?这时分就能够运用SynchronousQueue
了。
SynchronousQueue
被称为同步行列
,当生产者往行列中放元素的时分,有必要等候顾客把这个元素取走,否则一向堵塞。顾客取元素的时分,同理也有必要等候生产者放行列中放元素。
-
SynchronousQueue
底层有两种完成办法,别离是根据栈完成非公正战略,以及根据行列完成的公正战略。 -
SynchronousQueue
初始化的时分,能够指定运用公正战略还是非公正战略。 -
SynchronousQueue
不存储元素,不适合作为缓存行列运用。适用于生产者与顾客速度相匹配的场景,可减少使命履行的等候时刻。
更多概况点击:SynchronousQueue源码分析
PriorityBlockingQueue
由于PriorityQueue
跟前几个堵塞行列不一样,并没有完成BlockingQueue
接口,只是完成了Queue
接口,所以PriorityQueue
并不算堵塞行列。Queue
接口中界说了几组放数据和取数据的办法,来满意不同的场景。
-
PriorityQueue
完成了Queue
接口,供给了两组放数据和读数据的办法,来满意不同的场景。 -
PriorityQueue
底层根据数组完成,完成了依照元素值巨细排序的功用,内部依照最小堆存储,完成了高效的插入和删去。 -
PriorityQueue
初始化的时分,能够指定数组长度和自界说比较器。 -
PriorityQueue
初始容量是11,当数组容量小于64,选用2倍扩容,否则选用1.5扩容。 -
PriorityQueue
每次都是从数组头节点取元素,取之后需要调整最小堆。
更多概况点击:PriorityBlockingQueue源码分析
DelayQueue
DelayQueue
是一种本地推迟行列,比如希望咱们的使命在5秒后履行,就能够运用DelayQueue
完成。常见的运用场景有:
- 订单10分钟内未支付,就撤销。
- 缓存过期后,就删去。
- 消息的推迟发送等。
-
DelayQueue
底层选用组合的办法,复用PriorityQueue
的依照推迟时刻排序使命的功用,完成了推迟行列。 -
DelayQueue
是线程安全的,内部运用ReentrantLock
加锁。
更多概况点击:DelayQueue源码分析
总结
这5种堵塞行列的特性各不相同,在运用的时分该怎样选择呢?我做了一张图,供咱们参阅。
关注我,咱们一同接着分析Java源码,学习更多Java面试干货。