深入理解并发类库中提供线程安全队列

问题:

并发包中ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别?

知识点

  • Java并发类库中提供的各种各样的线程安全队列

《深入理解并发类库中提供线程安全队列》 image

  • BlockingQueue提供了特定的等待性操作,获取(take)等待元素进队,或者插入(put)等待队列出现空位。

源码:

 /**
     * Retrieves and removes the head of this queue, waiting if necessary
     * until an element becomes available.
     *
     * @return the head of this queue
     * @throws InterruptedException if interrupted while waiting
     */
    E take() throws InterruptedException;
 /**
     * Inserts the specified element into this queue, waiting if necessary
     * for space to become available.
     *
     * @param e the element to add
     * @throws InterruptedException if interrupted while waiting
     * @throws ClassCastException if the class of the specified element
     *         prevents it from being added to this queue
     * @throws NullPointerException if the specified element is null
     * @throws IllegalArgumentException if some property of the specified
     *         element prevents it from being added to this queue
     */
    void put(E e) throws InterruptedException;
  • ArrayBlockQueue是典型的有界队列,其内部是以final的数组保存数据,数组的大小就决定了队列的边界,所以我们在创建ArrayBlockingQueue时,都指定容量,如:
/**
     * Creates an {@code ArrayBlockingQueue} with the given (fixed)
     * capacity and default access policy.
     *
     * @param capacity the capacity of this queue
     * @throws IllegalArgumentException if {@code capacity < 1}
     */
    public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }

  • LinkedBlockingQueue,容易被误解为无边界,但其行为和内部代码都是基于有界的逻辑实现的,只不过如果我们没有在创建队列时就指定容量,那么其容量限制就自动设置Integer.MAX_VALUE,称为了无界队列。
 /**
     * Creates a {@code LinkedBlockingQueue} with a capacity of
     * {@link Integer#MAX_VALUE}.
     */
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }
  • SynchronousQueue,这是一个非常奇葩的队列实现,每个删除操作都要等待插入操作,反之每个插入操作都要等待删除动作。那么这个队列的容量是多少呢?是1吗?其实不是的,其内部容量是0。
  • PriorityBlockingQueue是无边界的优先队列,虽然严格意义上讲,其大小总归是手系统资源影响。
  • DelayQueue和LinkedTransferQueue同样是无边界的队列。对于无边界的队列,有一个自然的结果,就是put操作永远也不会发生其他BlockingQueue的那种等待情况。

回答问题:

有时候我们把并发包下面的所有容器都习惯叫做并发容器,但是严格来讲,类似ConcurrentLinkedQueue这种“Concurrent”容器,才是真正代表并发。
关于问题中它们的区别:

  • Concurrent类型基于lock-free,在常见的多线程访问场景,一般可以提供较高吞吐量。
  • 而LinkedBlockingQueue内部则是基于锁,并提供了BlockingQueue的等待性方法。
    不知道你有没有注意到,java.util.concurrent包提供的容器(Queue、List、Set)、Map,从命名上可以大概区分为Concurrent*、CopyOnWrite和Blocking等三类,同样是线程安全容器,可以简单认为:
  • Concurrent类型没有类似CopyOnWrite之类容器较重的修改开销。
  • 但是,凡是都是有代价的,Concurrent往往提供了较低的遍历一致性,你可以这样理解所谓的弱一致性,例如,当李勇迭代器遍历时,如果容器发生修改,迭代器仍然可以继续进行遍历。
  • 与弱一致性对应的,就是我介绍过的同步容器常见的行为“fail-fast”,也就是检测到容器在遍历过程中发生了修改,则抛出ConcurrentModificationException,不再继续遍历。
  • 弱一致性的另外一个体现是,size等操作准确性是有线的,未必是100%准确。
  • 与此同时,读取的性能具有一定的不确定性。

参考:

  • 队列中部分源码
  • 极客时间APP核心技术第20讲| 并发包中ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别?

声明:此为原创,转载请联系作者

作者:微信公众号添加公众号-遛狗的程序员 ,或者可以扫描以下二维码关注相关技术文章。

《深入理解并发类库中提供线程安全队列》 qrcode_for_gh_1ba0785324d6_430.jpg

当然喜爱技术,乐于分享的你也可以可以添加作者微信号:

《深入理解并发类库中提供线程安全队列》 WXCD.jpeg

    原文作者:遛狗的程序员
    原文地址: https://www.jianshu.com/p/54aa33d136b4
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞