文章字数:1260,阅读全文大约需要5分钟
JUC
包下的14个并发容器,专门应付并发状态下线程安全的问题
介绍
ConcurrentHashMap
并发版的HashMap
CopyOnWriteArrayList
并发版的ArrayList
CopyOnWriteArraySet
并发版的Set
ConcurrentLinkedQueue
基于链表的并发队列(不阻塞)ConcurrentLinkedDeque
基于双向列表的并发队列ConcurrentSkipListMap
基于跳表的并发Map
跳表:链表之上加了一层索引,就是一个新的链表,指向源数据的双数索引的位置。这一层的索引之上还可以增加索引,指向本层的双数位置。链表版的二分法。ConcurrentSkipListSet
基于跳表的并发Set
ArrayBlockingQueue
阻塞队列基于数组LinkedBlockingQueue
阻塞队列基于链表LinkedBlockingDeque
阻塞队列基于双链表PriorityBlockingQueue
线程安全的优先队列SynchronousQueue
读写成对的队列LinkedTransferQueue
基于链表的数据交换队列DelayQueue
延时队列
一、ConcurrentHashMap
最常见的并发容器之一,常作用于并发场景下的缓存。底层还是哈希表,但是java8
中有了优化。
java7
采用分段锁,即数据被分成16个部分。每个部分一把锁,各部分之间不冲突。java8
放弃了分段锁,采用CAS
乐观锁。java8
中增加了同哈希值组成的链表长度超过8之后会转换成红黑树
二、CopyOnWriteArrayList
并发版的ArrayList
, 底层结构还是数组。原理是增删改的操作会加锁,其中删改会创建新的数组并替换原来的。
适用于读多写少的情况,并且读没有加锁,所以可能读到脏数据。
读的效率高。
三、CopyOnWriteArraySet
并发版的Set
, 内部使用CopyOnWriteArrayList
实现的。每次add
都会遍历内部数据,检查是否重复。不存在执行插入(加锁)
和CopyOnWriteArrayList
注意点类似,多了一条数据量不能太大,否则遍历成本过高。
四、ConcurrentLinkedQueue
基于链表实现的并发队列,不阻塞。使用乐观锁CAS
保证现存的安全。内部是链表,理论上没有大小限制。
五、ConcurrentLinkedDeque
基于双向链表的并发队列,可以分别对于首尾进行操作。可以先进先出也可以先进后出。
六、ConcurrentSkipListMap
基于跳表SkipList
的并发Map
, 跳表是用空间换时间的数据结构。
每一层都是上一层的一半数据,类似于二分查找的实现方式来增加搜索效率。
七、ConcurrentSkipListSet
基于跳表的并发Set
, 使用ConcurrentSkipListMap
实现的
八、ArrayBlockingQueue
基于数组的阻塞队列,构造时需要指定大小。
添加元素时如果数组满了会阻塞,知道有位置可以放。(也可以设置返回或者超时等待)
通过锁ReentrantLock
保证线程安全
九、LinkedBlockingQueue
基于链表的阻塞队列,相比不阻塞ConcurrentLinkedQueue
多了容量的现在。不设置默认int
的最大值
十、LinkedBlockingDeque
和LinkedBlockingQueue
类似,底层是双链表
十一、PriorityBlockingQueue
线程安全的优先队列,构造的时候需要传入一个比较器。内部会根据元素的优先级排序。读取的时候会根据优先级从高到低读取。
优先级低的可能会因为一直有更高级的元素而无法被读取。
十二、SynchronousQueue
数据同步交换队列,内部只能存一个元素。每次插入操作必须要取才能再次插入。
任何一个对SynchronousQueue
写需要等到一个对SynchronousQueue
的读操作,反之亦然
十三、LinkedTransferQueue
基于链表的交换队列,比SynchronousQueue
更强大。
实现了TransferQueue
接口,通过transfer
方法放入元素时如果有线程在阻塞去元素,就会把元素直接给等待队列。如果没有人等待,则放到队列尾部,并阻塞直到有人读取。
十四、DelayQueue
可以使放入的元素在指定延时之后才被消费者取出,元素需要实现Delayed
接口