Java并发-并发容器
本文最后更新于:2 年前
同步容器
collections 中有一些方法
synchronizedlist:
synchronizedmap:
synchronizedcollection:
在遍历时容器发生了结构性变化,会报错。
写时复制的的 List 和 Set
copyonwritearraylist
copyonwritearrayset
线程安全
迭代器不支持修改
每次修改时都会新建一个数组,复制进去。读都会访问原来的数组。
基于 reentrantlock 实现
不适合大数组
适合大部分访问都是读的情况
ConcurrentHashMap
jdk 以前 concurrenthashmap 是分段的数组+链表实现。
实现线程安全的方式:
1,在 JDK1.7 的时候, ConcurrentHashMap (分段锁)对整个桶数组进⾏了分割分段,每⼀把锁只锁容器其中⼀部分数据,多线程访问容器⾥不同数据段的数据,就不会存在锁竞争,提⾼并发访问率。
一个 concurrenthashmap 里面包含一个 segment 数组。一个 segment 包含一个 hashentry 数组。
核心:
分段锁
读不需要锁
2,JDK1.8 的时候已经摒弃了 Segment 的概念,⽽是直接⽤ Node 数组+链表/红⿊树的数据结构来实现,并发控制使⽤ synchronized 和 CAS 来操作。
synchronized 只会锁定当前链表或者红黑树的首节点。效率大大提高。
迭代器弱一致性?
遍历时,内部元素变化发生在已经遍历过的部分,不会体现出来。
3,hashtable 是同一把锁,全表锁。
基于跳表的 Map 和 Set
concurrentskiplistmap==treemap
concurrentskiplistset==treeset
跳表更加容易实现高效并发
没有使用锁,所有的操作都可以并发
他们的 size()方法复杂度为 o(n),并且结果也不一定准确
跳表
基于链表+多层索引
类似二分查找
并发队列
无锁非阻塞:不用锁,所有操作都可以立即执行,主要通过循环 CAS 实现并发安全。
阻塞队列:使用锁和条件。很多操作都要获得锁或者满足条件再返回。不满足或者未获得锁就会阻塞。
都是弱一致性的。遍历时遍历过的内容改变了不会显示。
无锁非阻塞并发队列
concurrentlinkedqueue
cocurrentlinkeddeque
基于链表,没有大小限制
基于循环 cas
普通阻塞队列
都实现了接口 blockingqueue,在入队出队时可能会等待。
arrayblockingqueue 循环数组
linkedblockingqueue 单向链表
都是使用显示锁 reentrantlock 和显示条件 condition 实现的
优先级阻塞队列
按照优先级出队。