Java中的阻塞队列简介

生产者消费者模型相信大家都知道,我们使用普通的任务队列时要手动防止并发,代码比较繁琐,JDK给我们提供了线程安全的阻塞队列BlockingQueue来简化我们的代码。

总览

BlockingQueue是一个接口,他定义了阻塞队列中的总体行为,我们来看一下他的主要方法

他给我们提供了多种取出与存放操作,根据上面的图片就可以清楚的了解各种方法不同行为。阻塞队列并不允许存放null元素,阻塞队列分为有界和无界队列,有界即需要指定队列容量,无界则队列容量上限为Integer.MAX_VALUE。

JDK一共提供了7种BlockingQueue的实现类(其实在ScheduledThreadPoolExecutor中还有一个内部类,这里不讨论),我们分别简单介绍下。

1.ArrayBlockingQueue

由数组实现的有界阻塞队列,使用时必须指定容量大小。这里写顺便分析下他如何支持多线程的。先来看下他的几个实例域

主要通过上面的俩个条件Condition来实现生产者与消费者的协调,condition用来与lock共同实现线程的等待通知机制(也可以用原来的synchronized与object的wait和notify来实现)。接下来看入队的方法

这里以阻塞的put方法为例,首先获取锁,判断数组中元素是否已经满了,满了的话调用notFull的await方法,直到有消费者消费掉元素后调用notFull的signal方法后队列有剩余空间才能继续入队。数组有空闲容量的情况下调用enqueue方法,将元素放入数组后,调用notEmpty的signal方法,告诉消费者队列中已经有元素了,可以来消费了。

再来看与之对应的阻塞的take方法

逻辑基本就是和put方法相反的,数组为空时,await等待,直到队列有元素时notEmpty.notify通知进行消费,消费之后通知生产者队列有空余位置。

2.DelayQueue

无界延时队列,他可以在指定时间后取出任务。

可以看到,该队列中的元素必须继承Delayed接口,而Delayed又继承了Comparable接口,所以元素就必须实现Delayed中的getDelay方法用于获取延时时间,与Comparable中的compareTo方法用于比较时间。

put的时候其实是调用了PriorityQueue的offer方法,PriorityQueue为优先级队列,底层由堆实现(对堆不太了解的小伙伴建议阅读红色算法书第四版,比那本经典的算法要简单一些而且书中的数据结构都是使用java实现的),DelayQueue将最先执行的任务放在队头。

取出方法如图,首先取出队头元素判断delay时间是否小于等于0,若是则代表立即执行,若不是调用condition的awaitNanos等待相应时间后再执行。这里getDelay方法的实现是比较重要的

网图

基本上是按照这样的模式来实现自己的延时任务类。

3.LinkedBlockingDeque

基于链表实现的可选择界限的双端阻塞队列,可选择界限意味着可以在构造函数中指定容量,也可以不指定,不指定意味着容量为Integer.MAX_VALUE.

4.LinkedBlockingQueue

基于链表实现的可选择界限的阻塞队列

5.LinkedTransferQueue

引用自http://ifeve.com/java-blocking-queue/:基于链表实现的无界Transfer队列。TransferQueue是一个生产者有可能等待消费者去接收的阻塞队列,它主要有俩个方法,transfer:如果当前有消费者正在等待接收元素(消费者使用take()方法或带时间限制的poll()方法时),transfer方法可以把生产者传入的元素立刻transfer给消费者。如果没有消费者在等待接收元素,transfer方法会将元素存放在队列的tail节点,并等到该元素被消费者消费了才返回;tryTransfer:用来试探生产者传入的元素是否能直接传给消费者。如果没有消费者等待接收元素,则返回false。和transfer方法的区别是tryTransfer方法无论消费者是否接收,方法立即返回。而transfer方法是必须等到消费者消费了才返回。对于带有时间限制的tryTransfer(E e, long timeout, TimeUnit unit)方法,则是试图把生产者传入的元素直接传给消费者,但是如果没有消费者消费该元素则等待指定的时间再返回,如果超时还没消费元素,则返回false,如果在超时时间内消费了元素,则返回true。

6.PriorityBlockingQueue

基于堆实现的优先级阻塞队列,默认按元素的自然顺序排序。

7.SynchronousQueue

每个插入操作必须等待另一个线程的相应移除操作的队列,该队列是不能存储元素的,可以看成是一个传送带。降低了将数据从生产者移动到消费者的延迟。

最后:我写着写着就总感觉和以前看过的一篇文章好像雷同了(尴尬),找了好久终于找到那篇博文了http://ifeve.com/java-blocking-queue/,果然是受了这篇文章的影响,这篇文章作者是蚂蚁金服技术专家方腾飞。

你可能感兴趣的:(Java中的阻塞队列简介)