Java并发-并发容器

本文最后更新于:2 年前

同步容器

collections 中有一些方法

synchronizedlist:

synchronizedmap:

synchronizedcollection:

在遍历时容器发生了结构性变化,会报错。

写时复制的的 List 和 Set

copyonwritearraylist

copyonwritearrayset

线程安全

迭代器不支持修改

每次修改时都会新建一个数组,复制进去。读都会访问原来的数组。

基于 reentrantlock 实现

不适合大数组

适合大部分访问都是读的情况

ConcurrentHashMap

jdk 以前 concurrenthashmap 是分段的数组+链表实现。
实现线程安全的方式:

1,在 JDK1.7 的时候, ConcurrentHashMap (分段锁)对整个桶数组进⾏了分割分段,每⼀把锁只锁容器其中⼀部分数据,多线程访问容器⾥不同数据段的数据,就不会存在锁竞争,提⾼并发访问率
一个 concurrenthashmap 里面包含一个 segment 数组。一个 segment 包含一个 hashentry 数组。

核心:

分段锁

读不需要锁

image-20210803181948397

2,JDK1.8 的时候已经摒弃了 Segment 的概念,⽽是直接⽤ Node 数组+链表/红⿊树的数据结构来实现,并发控制使⽤ synchronized 和 CAS 来操作。
synchronized 只会锁定当前链表或者红黑树的首节点。效率大大提高。

迭代器弱一致性?

遍历时,内部元素变化发生在已经遍历过的部分,不会体现出来。

image-20210803182140097

3,hashtable 是同一把锁,全表锁。

基于跳表的 Map 和 Set

concurrentskiplistmap==treemap

concurrentskiplistset==treeset

跳表更加容易实现高效并发

没有使用锁,所有的操作都可以并发

他们的 size()方法复杂度为 o(n),并且结果也不一定准确

跳表

基于链表+多层索引

类似二分查找

并发队列

无锁非阻塞:不用锁,所有操作都可以立即执行,主要通过循环 CAS 实现并发安全。

阻塞队列:使用锁和条件。很多操作都要获得锁或者满足条件再返回。不满足或者未获得锁就会阻塞。

都是弱一致性的。遍历时遍历过的内容改变了不会显示。

无锁非阻塞并发队列

concurrentlinkedqueue

cocurrentlinkeddeque

基于链表,没有大小限制

基于循环 cas

普通阻塞队列

都实现了接口 blockingqueue,在入队出队时可能会等待。

image-20210803184553784

arrayblockingqueue 循环数组

linkedblockingqueue 单向链表

都是使用显示锁 reentrantlock 和显示条件 condition 实现的

优先级阻塞队列

按照优先级出队。

延时阻塞队列

其他