导言

最近一个月一向在更新《解读Java源码专栏》,其中跟咱们一同分析了Java的常见的5种BlockingQueue(堵塞行列),今日就盘点一下这几种堵塞行列的优缺点、差异,以及运用场景。

常见的BlockingQueue有以下5种,下面会详细介绍。

  • ArrayBlockingQueue

根据数组完成的堵塞行列,创立行列时需指定容量巨细,是有界行列。

  • LinkedBlockingQueue

根据链表完成的堵塞行列,默认是无界行列,创立能够指定容量巨细

  • SynchronousQueue

一种没有缓冲的堵塞行列,生产出的数据需要立刻被消费

  • PriorityBlockingQueue

完成了优先级的堵塞行列,能够依照元素巨细排序,是无界行列

  • DelayQueue

完成了推迟功用的堵塞行列,根据PriorityQueue完成的,是无界行列

BlockingQueue简介

这几种堵塞行列都是完成了BlockingQueue接口,在日常开发中,咱们如同很少用到BlockingQueue(堵塞行列)BlockingQueue到底有什么效果?运用场景是什么样的? 假如运用过线程池或者阅览过线程池源码,就会知道线程池的核心功用都是根据BlockingQueue完成的。

咱们用过消息行列(MessageQueue),就知道消息行列效果是解耦、异步、削峰。相同BlockingQueue的效果也是这三种,差异是BlockingQueue只效果于本机器,而消息行列相当于分布式BlockingQueue

还不了解Java的5大BlockingQueue堵塞行列源码,看这篇文章就够了
BlockingQueue作为堵塞行列,主要运用于生产者-顾客形式的场景,在并发多线程中尤其常用。

  1. 比如像线程池中的使命调度场景,提交使命和拉取并履行使命。
  2. 生产者与顾客解耦的场景,生产者把数据放到行列中,顾客从行列中取数据进行消费。两者进行解耦,不用感知对方的存在。
  3. 应对突发流量的场景,事务高峰期忽然来了很多请求,能够放到行列中缓存起来,顾客以正常的频率从行列中拉取并消费数据,起到削峰的效果。

BlockingQueue是个接口,界说了几组放数据和取数据的办法,来满意不同的场景。

操作 抛出反常 回来特定值 堵塞 堵塞一段时刻
放数据 add() offer() put() offer(e, time, unit)
取数据(一同删去数据) remove() poll() take() poll(time, unit)
取数据(不删去) element() peek() 不支持 不支持

这四组办法的差异是:

  1. 当行列满的时分,再次添加数据,add()会抛出反常,offer()会回来false,put()会一向堵塞,offer(e, time, unit)会堵塞指定时刻,然后回来false。
  2. 当行列为空的时分,再次取数据,remove()会抛出反常,poll()会回来null,take()会一向堵塞,poll(time, unit)会堵塞指定时刻,然后回来null。

ArrayBlockingQueue

  1. ArrayBlockingQueue底层根据数组完成,选用循环数组,提升了数组的空间利用率。
  2. ArrayBlockingQueue初始化的时分,有必要指定行列长度,是有界的堵塞行列,所以要预估好行列长度,确保生产者和顾客速率相匹配。
  3. ArrayBlockingQueue的办法是线程安全的,运用ReentrantLock在操作前后加锁来确保线程安全。

更多概况点击:ArrayBlockingQueue源码分析

LinkedBlockingQueue

  1. LinkedBlockingQueue底层根据链表完成,支持从头部弹出数据,从尾部添加数据。
  2. LinkedBlockingQueue初始化的时分,假如不指定行列长度,默认长度是Integer最大值,相当于无界行列,有内存溢出危险,主张初始化的时分指定行列长度。
  3. LinkedBlockingQueue的办法是线程安全的,别离运用了读写两把锁,比ArrayBlockingQueue功能更好。

ArrayBlockingQueue差异是:

  1. 底层结构不同,ArrayBlockingQueue底层根据数组完成,初始化的时分有必要指定数组长度,无法扩容。LinkedBlockingQueue底层根据链表完成,链表最大长度是Integer最大值。
  2. 占用内存巨细不同,ArrayBlockingQueue一旦初始化,数组长度就确认了,不会跟着元素添加而改动。LinkedBlockingQueue会跟着元素越多,链表越长,占用内存越大。
  3. 功能不同,ArrayBlockingQueue的入队和出队共用一把锁,并发较低。LinkedBlockingQueue入队和出队运用两把独立的锁,并发情况下功能更高。
  4. 公正锁选项,ArrayBlockingQueue初始化的时分,能够指定运用公正锁或者非公正锁,公正锁形式下,能够依照线程等候的顺序来操作行列。LinkedBlockingQueue只支持非公正锁。
  5. 适用场景不同,ArrayBlockingQueue适用于明确限制行列巨细的场景,避免生产速度大于消费速度的时分,造成内存溢出、资源耗尽。LinkedBlockingQueue适用于事务高峰期能够主动扩展消费速度的场景。

更多概况点击:LinkedBlockingQueue源码分析

SynchronousQueue

无论是ArrayBlockingQueue还是LinkedBlockingQueue都是起到缓冲行列的效果,当顾客的消费速度跟不上时,使命就在行列中堆积,需要等候顾客渐渐消费。 假如咱们想要自己的使命快速履行,不要积压在行列中,该怎样办?这时分就能够运用SynchronousQueue了。 SynchronousQueue被称为同步行列,当生产者往行列中放元素的时分,有必要等候顾客把这个元素取走,否则一向堵塞。顾客取元素的时分,同理也有必要等候生产者放行列中放元素。

  1. SynchronousQueue底层有两种完成办法,别离是根据栈完成非公正战略,以及根据行列完成的公正战略。
  2. SynchronousQueue初始化的时分,能够指定运用公正战略还是非公正战略。
  3. SynchronousQueue不存储元素,不适合作为缓存行列运用。适用于生产者与顾客速度相匹配的场景,可减少使命履行的等候时刻。

更多概况点击:SynchronousQueue源码分析

PriorityBlockingQueue

由于PriorityQueue跟前几个堵塞行列不一样,并没有完成BlockingQueue接口,只是完成了Queue接口,所以PriorityQueue并不算堵塞行列。Queue接口中界说了几组放数据和取数据的办法,来满意不同的场景。

  1. PriorityQueue完成了Queue接口,供给了两组放数据和读数据的办法,来满意不同的场景。
  2. PriorityQueue底层根据数组完成,完成了依照元素值巨细排序的功用,内部依照最小堆存储,完成了高效的插入和删去。
  3. PriorityQueue初始化的时分,能够指定数组长度和自界说比较器。
  4. PriorityQueue初始容量是11,当数组容量小于64,选用2倍扩容,否则选用1.5扩容。
  5. PriorityQueue每次都是从数组头节点取元素,取之后需要调整最小堆。

更多概况点击:PriorityBlockingQueue源码分析

DelayQueue

DelayQueue是一种本地推迟行列,比如希望咱们的使命在5秒后履行,就能够运用DelayQueue完成。常见的运用场景有:

  • 订单10分钟内未支付,就撤销。
  • 缓存过期后,就删去。
  • 消息的推迟发送等。
  1. DelayQueue底层选用组合的办法,复用PriorityQueue的依照推迟时刻排序使命的功用,完成了推迟行列。
  2. DelayQueue是线程安全的,内部运用ReentrantLock加锁。

更多概况点击:DelayQueue源码分析

总结

这5种堵塞行列的特性各不相同,在运用的时分该怎样选择呢?我做了一张图,供咱们参阅。

还不了解Java的5大BlockingQueue堵塞行列源码,看这篇文章就够了
关注我,咱们一同接着分析Java源码,学习更多Java面试干货。