并发包中的ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别?
- Concurrent 类型基于lock-free,在常见的多线程访问场景,一般可以提供较高吞吐量;
ConcurrentLinkedQueue 是基于CAS 的无锁技术,不需要在每个操作时使用锁,所以扩展性表现更加优异。
- 而LinkedBlockingQueue 内部则是基于锁,并提供了BlockingQueue的等待性方法;
- Blocking等待性方法:获取时(take)等待元素进队,或者插入时(put)等待队列出现空位。
- BlockingQueue 基本都是基于锁实现的。
java.util.concurrent 包提供的容器Queue、List、Set、Map,从命名上可以大概区分为:
- Concurrent
- CopyOnWrite
- Blocking
等三类。同样是线程安全容器,可以简单认为:
- Concurrent 类型没有类似 CopyOnWrite 之类容器相对较重的修改开销。
- 但是,Concurrent 往往提供了较低的遍历一致性(或者说弱一致性)。当利用迭代器遍历时,如果容器发生修改,迭代器仍然可以继续进行遍历。
- 弱一致性的另外一个体现是,size 等操作准确性是有限的,未必100%准确。
Java 并发类库提供的各种各样的线程安全队列实现
有两个特别的Deque实现,ConcurrentLinkedDeque 和LinkedBlockingDeque。Deque 的侧重点是支持对队列头尾都进行插入和删除操作。方法如下:
- 尾部插入时:addLast(e), offerLast(e)
- 尾部删除时:removeLast(), pollLast()
从行为特征来看,绝大部分Queue 都实现了 BlockingQueue 接口。在常规队列操作基础上,Blocking 意味着其提供了特定的等待性操作,获取时(take)等待元素进队,或者插入时(put)等待队列出现空位。
public class LinkedBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable
BlockingQueue 经常被考察的点,就是是否有界(Bounded、Unbounded)?
- ArrayBlockingQueue 是最典型的有界队列,其内部以final 的数组保存数据,数组的大小就决定了队列的边界,所以我们在创建 ArrayBlockingQueue 时,都需要指定容量。
- LinkedBlockingQueue,如果在创建队列时就没有指定容量,那么其容量限制就自动被设置为Integer.MAX_VALUE,成为了无界队列。否则,就是有界队列。
- SynchronousQueue,这是一个非常奇葩的队列实现,没有数据缓存的BlockingQueue。每一个take操作都需要等待put操作,反之亦然。其内部容量是0。
- PriorityBlockingQueue 是无边界的优先队列,容量大小受系统资源影响。
- DelayedQueue 和 LinkedTransferQueue 同样是无边界的队列。对于无边界队列,有一个自然的结果,就是 put 操作永远不会发生 Blocking 等待情况。