从Java 5 开始,在java.util.concurrent 包下提供了大量支持高效并发访问的集合接口和实现类,如下图所示:
以CopyOnWrite开头的集合即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往容器添加,而是先将容器进行Copy,复制出一个新的容器,然后往新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
以Concurrent开头的集合类代表了支持并发访问的集合,它们可以支持多个线程并发写入访问,这些写入线程的所有操作都是线程安全的,但读取操作不必锁定。以Concurrent 开头的集合类采用了更复杂的算法来保证永远不会锁住整个集合,因此在并发写入时有较好的性能。
下列是并发集合列表:
接口 | 类 | 特性 | 适用场景 |
Queue | ArrayBlockingQueue | 有界、阻塞、线程安全、FIFO | 生产者、消费者场景比较合适,并且支持FIFO |
Queue | LinkedTransferQueue | 阻塞、线程安全、FIFO | |
Queue | PriorityBlockingQueue | 阻塞、优先级 | 要使用FIFO,您需要插入一个新的FIFOEntry(anEntry),而不是普通的entry对象 |
Queue | SynchronousQueue | 阻塞、FIFO、线程安全、同步队列 | 只允许一个值添加、取出,无容量 |
Queue | ConcurrentLinkedQueue | 线程安全、FIFO | 线程安全,但一边遍历一边poll还是不行的 |
Deque | ConcurrentLinkedDeque | 线程安全、链表 | 链表式操作,可对队首队尾直接操作 |
Map | ConcurrentHashMap | 非阻塞、线程安全 | |
Map | ConcurrentSkipListMap | 线程安全 | 构造函数支持排序,甚至可按照复合类型字段排序 |
Map | ConcurrentSkipListSet | 线程安全 | 构造函数支持排序,甚至可按照复合类型字段排序 |
List | CopyOnWriteArrayList | 线程安全 | 1、读写分离,读和写分开,需要读和写时都是对拷贝的副本进行操作。 2、最终一致性 缺点: 1、由于写操作的时候,需要拷贝数组,会消耗内存,如果原数组的内容比较多的情况下,可能导致young gc或者full gc 2、不能用于实时读的场景,像拷贝数组、新增元素都需要时间,所以调用一个set操作后,读取到数据可能还是旧的,虽然CopyOnWriteArrayList 能做到最终一致性,但是还是没法满足实时性要求; 3、CopyOnWriteArrayList 合适读多写少的场景,不过这类慎用 因为谁也没法保证CopyOnWriteArrayList 到底要放置多少数据,万一数据稍微有点多,每次add/set都要重新复制数组,这个代价实在太高昂了。在高性能的互联网应用中,这种操作分分钟引起故障。 |
Set | CopyOnWriteArraySet | 线程安全 | 1、读写分离,读和写分开,需要读和写时都是对拷贝的副本进行操作。 2、不存储重复对象 3、最终一致性 |
Queue | DelayQueue | 阻塞、线程安全、FIFO | 某对象在getDelay方法返回0或者负数时,才能从take方法中获取到值,否则一直阻塞 |
Queue | LinkedBlockingQueue | 有界、阻塞、线程安全、FIFO、链表 | |
Queue | LinkedBlockingDeque | 有界、阻塞、线程安全、FIFO、链表 |