阻塞队列简介

1,048 阅读4分钟

概述

阻塞队列是并发编程中常用的类,而且是线程池的基础,Jdk中有七个阻塞队列的类,本篇我们一起对比下它们的区别和使用场景,一起看公共接口BlockingDeque,看不同方法的区别。阻塞队列测试代码可在github中下载

阻塞队列对比

  1. ArrayBlockingQueue: 是用数组实现的有界阻塞队列,初始化时必须传入容量,是FIFO队列,支持公平和非公平锁。
  2. LinkedBlockingQueue:是用链表实现的有界阻塞队列,初始化时如果没有传入容量,则容量时Intger.MAX_VALUE,是FIFO队列,因为入队和出队方法各是一把锁,所以一般情况下并发性能优于ArrayBlockingQueue。
  3. LinkedBlockingDeque:是双向链表实现的有界阻塞队列,初始化时如果没有传入容量,则容量时Intger.MAX_VALUE,可以实现FIFO队列,也可以实现LIFO队列。
  4. PriorityBlockingQueue:是数组实现的具有优先级的无界阻塞队列,有两个注意事项,一:该队列是具有优先级的,不是按先进先出或者后进先出,是按优先级的高低,所以入队的对象必须是实现了Comparable接口的。二:队列虽然逻辑上是无界的,但因为是数组实现的,不能无限扩容,作者在代码里限制了,最大容量是Intger.MAX_VALUE-8。
  5. DelayQueue:延迟队列可以指定多久才能从队列中获取当前元素。只有延时期满后才能从队列中获取元素, 元素必须是Delayed的子类
  6. SynchronousQueue:不存储元素的阻塞队列,每一个put操作必须等待take操作,否则不能添加元素。支持公平锁和非公平锁。
  7. LinkedTransferQueue:由链表结构组成的无界阻塞队列,对比LinkedBlockingQueue除了有put、take等方法外,还提供了transfer方法,该方法会阻塞线程,直到元素被消费,才返回。

上面列举了阻塞队列的异同点,我们可以根据自己的业务场景选择合适的阻塞队列。

BlockingQueue

我们知道接口提供了一套规范,作者Doug lea 给所有的阻塞队列制定了一套规范,规定了入队、出队不同情况下的方法,我们看各个阻塞队列的具体实现之前,先看看阻塞队列的共同接口BlockingQueue

/**
 * 注意下面的入队失败,什么情况发生?
 *  对,当队列有界时,队列元素满了,就会发生入队失败的情况
 *      所以如LinkedTransferQueue这种无界队列没有入队失败一说,你会发现add offer put方法逻辑是一样的
 */
public interface BlockingQueue<E> extends Queue<E> {
    //入队,入队失败会抛出异常
    boolean add(E e);

    //入队,入队成功返回true,否则返回false
    boolean offer(E e);

    //入队,入队成功返回,否则进行等待
    void put(E e) throws InterruptedException;

    //入队,不成功等待一段时间,仍然不能入队成功返回false,入队成功返回true
    boolean offer(E e, long timeout, TimeUnit unit)
            throws InterruptedException;

    //出队,队列为空进行等待,否则将元素出队
    E take() throws InterruptedException;

    //出队,队列为空进行一段时间的等待,仍然为空返回null,否则将元素出队
    E poll(long timeout, TimeUnit unit)
            throws InterruptedException;

    //返回剩余余量
    int remainingCapacity();

    //将某个元素出队,存在返回true,否则返回false
    boolean remove(Object o);

    //判断是否包含某个元素
    public boolean contains(Object o);

    //将队列中所有可用元素出队到集合C中
    int drainTo(Collection<? super E> c);

    //将队列中最多maxElements个可用元素出队到集合C中
    int drainTo(Collection<? super E> c, int maxElements);
}

上面是接口每个方法的简单注释。我们可以看到针对入队出队的不同场景,作者定义了不同的方法,是不是很棒,大部分场景都覆盖了。
add, offer, put 都是入队方法,只是队列满时的结果不相同。
take, poll都是出队方法,只是队列为空时,是否等待。
remove是移出指定元素。
drainTo 是批量移出。

结束语

通过本篇介绍我们根据业务场景可以选择不同的阻塞队列,是先进先出还是后进先出,是延迟操作还是及时操作等。
我们可以根据需要选择每个类不同的方法,入队失败是等待还是异常,出队时为空是等待还是直接返回null。
下一篇让我们一起看两个具体类的实现。