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

问题:

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

知识点

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

image

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

源码:

1
2
3
4
5
6
7
8
/**
* 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;
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 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时,都指定容量,如:
1
2
3
4
5
6
7
8
9
10
/**
* 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,称为了无界队列。
1
2
3
4
5
6
7
/**
* 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

文章目录
  1. 1. 问题:
  2. 2. 知识点
  3. 3. 回答问题:
|