并发编程系列:阻塞队列的实现原理
一 定义
阻塞队列(Blocking Queue)是一个支持两个附加操作:阻塞的插入,和阻塞的移除的队列。阻塞插入:当队列满时,队列会阻塞插入元素的线程,直到队列不满;阻塞移除:队列为空时,获取元素的线程会等待队列为非空。
从定义中,可以看到很接近我们所了解的生产者-消费者模式,事实上,也确实如此。阻塞队列就是生产者用来存放元素,消费者用来获取元素的容器。
二 操作
插入和移除操作的处理方式如下图所示:
关于异常,阻塞队列的异常发生在队列满和对列空的情况。队列满继续插入时,会抛 IllegalStateException 异常;队列空时,从队列取元素时会抛 NoSuchElementException 异常。
当阻塞队列满时,如果生产者线程往队列里 put 元素,队列会一直阻塞生产者线程,知道队列可用或者响应中断退出;
队列空时,如果消费者线程从队列里取(take)元素,队列会阻塞住消费者线程,直到队列非空。
超时退出:当阻塞队列满时,如果生产者线程往队列里插入元素,队列会阻塞生产者线程一段时间,如果超过指定时间,生产者线程就会退出。
注:如果是无界阻塞队列,那么就不会出现满队列的情况,使用 put 或 offer 方法永远不会阻塞。但可能会导致资源耗费过多,所以需要慎用!
三 JDK 的阻塞队列
ArrayBlockingQueue:数组实现的有界阻塞队列。先进先出原则对队列内元素排序
LinkedBlockingQueue:用链表实现的有界阻塞队列。队列的默认和最大长度为 Integer.MAX_VALUE。队列也是按照先入先出原则对元素进行排序。
PriorityBlockingQueue:支持优先级的无界阻塞队列。默认情况下元素使用自然顺序生序排序,但也支持自定义 compareTo()方法指定排序规则,或在构造方法中传入 Comparator。
DelayQueue:支持延时获取元素的无界阻塞队列,队列使用的 PriorityQueue 实现,并且必须实现 Delayed 接口,指定延迟时间。
SynchronousQueue:不存储元素的阻塞队列,每个 put 必须等待一个 take,否则不能继续添加元素。
LinkedTransferQueue:链表结构组成的 TransferQueue,支持 transfer 给消费者操作。
LinkedBlockingDeque:链表结构组成的双向阻塞队列,即队列的两端都支持插入和移除元素。
四 实现原理
从前面的内容可知,阻塞队列实现的最大难点是阻塞,包括阻塞的时机,以及具体的实现方式,同时需要保证生产者和消费者的信息感知。
JDK 中的实现,是使用通知模式,也就是当生产者往满队列中添加元素时,会阻塞生产者,消费者消费一个元素后,通知生产者当前队列可用。以 ArrayBlockingQueue 为例,JDK 中源码实现如下:
通知生产者:
队列不可用阻塞生产者,使用 LockSupport.park(this):
版权声明: 本文为 InfoQ 作者【程序员架构进阶】的原创文章。
原文链接:【http://xie.infoq.cn/article/5f2a350c20b1af7f640142b02】。文章转载请联系作者。
评论