Concurrent 类型基于 lock-free,在常见的多线程访问场景,一般可以提供较高吞吐量。

    LinkedBlockingQueue 内部则是基于锁,并提供了 BlockingQueue 的等待性方法。

    java.util.concurrent 包提供的容器(Queue、List、Set)、Map,从命名上可以大概区分为 Concurrent*、CopyOnWrite 和 Blocking等三类,同样是线程安全容器,可以简单认为:

    • Concurrent 类型没有类似 CopyOnWrite 之类容器相对较重的修改开销
    • 但是,凡事都是有代价的,Concurrent 往往提供了较低的遍历一致性。
    • 你可以这样理解所谓的弱一致性,例如,当利用迭代器遍历时,如果容器发生修改,迭代器仍然可以继续进行遍历。
    • 与弱一致性对应的,就是我介绍过的同步容器常见的行为“fail-fast”,也就是检测到容器在遍历过程中发生了修改,则抛出 ConcurrentModificationException,不再继续遍历。
    • 弱一致性的另外一个体现是,size 等操作准确性是有限的,未必是 100% 准确。
    • 与此同时,读取的性能具有一定的不确定性。

    image.png
    ArrayBlockingQueue 是最典型的的有界队列,其内部以 final 的数组保存数据,数组的大小就决定了队列的边界,所以我们在创建 ArrayBlockingQueue 时,都要指定容量,如:
    public ArrayBlockingQueue(int capacity, boolean fair)

    LinkedBlockingQueue,容易被误解为无边界,但其实其行为和内部代码都是基于有界的逻辑实现的,只不过如果我们没有在创建队列时就指定容量,那么其容量限制就自动被设置为 Integer.MAX_VALUE,成为了无界队列。

    SynchronousQueue,这是一个非常奇葩的队列实现,每个删除操作都要等待插入操作,反之每个插入操作也都要等待删除动作。那么这个队列的容量是多少呢?是 1 吗?其实不是的,其内部容量是 0。

    PriorityBlockingQueue是无边界的优先队列,虽然严格意义上来讲,其大小总归是要受系统资源影响。

    DelayedQueueLinkedTransferQueue 同样是无边界的队列。对于无边界的队列,有一个自然的结果,就是 put 操作永远也不会发生其他 BlockingQueue 的那种等待情况。

    BlockingQueue 基本都是基于锁实现

    1. /** Lock held by take, poll, etc */
    2. private final ReentrantLock takeLock = new ReentrantLock();
    3. /** Wait queue for waiting takes */
    4. private final Condition notEmpty = takeLock.newCondition();
    5. /** Lock held by put, offer, etc */
    6. private final ReentrantLock putLock = new ReentrantLock();
    7. /** Wait queue for waiting puts */
    8. private final Condition notFull = putLock.newCondition();

    LinkedBlockingQueue 改进了锁操作的粒度,头、尾操作使用不同的锁,所以在通用场景下,它的吞吐量相对要更好一些。