不允许还有Java程序员不了解BlockingQueue阻塞队列的实现原理

我们平时开发中好像很少使用到BlockingQueue(阻塞队列),比如我们想要存储一组数据的时候会使用ArrayList,想要存储键值对数据会使用HashMap,在什么场景下需要用到BlockingQueue呢?
1. BlockingQueue的应用场景当我们处理完一批数据之后,需要把这批数据发给下游方法接着处理,但是下游方法的处理速率不受控制,可能时快时慢 。如果下游方法的处理速率较慢,会拖慢当前方法的处理速率,这时候该怎么办呢?
你可能想到使用线程池,是个办法,不过需要创建很多线程,还要考虑下游方法支不支持并发,如果是CPU密集任务,可能多线程比单线程处理速度更慢,因为需要频繁上下文切换 。
这时候就可以考虑使用BlockingQueue,BlockingQueue最典型的应用场景就是上面这种生产者-消费者模型 。生产者往队列中放数据,消费者从队列中取数据,中间使用BlockingQueue做缓冲队列,也就解决了生产者和消费者速率不同步的问题 。

不允许还有Java程序员不了解BlockingQueue阻塞队列的实现原理

文章插图
你可能联想到了消息队列(MessageQueue),消息队列相当于分布式阻塞队列,而BlockingQueue相当于本地阻塞队列,只作用于本机器 。对应的是分布式缓存(比如:Redis、Memcache)和本地缓存(比如:Guava、Caffeine) 。
另外很多框架中都有BlockingQueue的影子,比如线程池中就用到BlockingQueue做任务的缓冲 。消息队列中发消息、拉取消息的方法也都借鉴了BlockingQueue,使用起来很相似 。
今天就一块深入剖析一下Queue的底层源码 。
2. BlockingQueue的用法BlockingQueue的用法非常简单,就是放数据和取数据 。
/** * @apiNote BlockingQueue示例 * @author 一灯架构 */public class Demo {public static void main(String[] args) throws InterruptedException {// 1. 创建队列,设置容量是10BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);// 2. 往队列中放数据queue.put(1);// 3. 从队列中取数据Integer result = queue.take();}}为了满足不同的使用场景,BlockingQueue设计了很多的放数据和取数据的方法 。
操作抛出异常返回特定值阻塞阻塞一段时间放数据addofferputoffer(e, time, unit)取数据removepolltakepoll(time, unit)取数据(不删除)element()peek()不支持不支持这几组方法的不同之处就是:
  1. 当队列满了,再往队列中放数据,add方法抛异常,offer方法返回false,put方法会一直阻塞(直到有其他线程从队列中取走数据),offer方法阻塞指定时间然后返回false 。
  2. 当队列是空,再从队列中取数据,remove方法抛异常,poll方法返回null,take方法会一直阻塞(直到有其他线程往队列中放数据),poll方法阻塞指定时间然后返回null 。
  3. 当队列是空,再去队列中查看数据(并不删除数据),element方法抛异常,peek方法返回null 。
工作中使用最多的就是offer、poll阻塞指定时间的方法 。
3. BlockingQueue实现类BlockingQueue常见的有下面5个实现类,主要是应用场景不同 。