首页 | 投资 | 产业 | 创投 | 保险 | 公司 | 企业 | 商业 | 消费 | 行情 | 经营 | 商品 | 基金 |
我慢再次聊聊并发编程:并发容器|每日关注

发稿时间:2023-07-03 09:27:26 来源: 今日头条
一、ConcurrentLinkedQueue/Deque

AQS内部的阻塞队列实现原理:基于双向链表,通过对head/tail进行CAS操作,实现入队和出队。

ConcurrentLinkedQueue 的实现原理和AQS 内部的阻塞队列类似:同样是基于 CAS,同样是通过 head/tail指针记录队列头部和尾部,但还是有稍许差别。

其次,在AQS的阻塞队列中,每次入队后,tail一定后移一个位置;每次出队,head一定后移一个 位置,以保证head指向队列头部,tail指向链表尾部。


(相关资料图)

但在ConcurrentLinkedQueue中,head/tail的更新可能落后于节点的入队和出队,因为它不是直 接对 head/tail指针进行 CAS操作的,而是对 Node中的 item进行操作。

二、ConcurrentHashMap

HashMap通常的实现方式是“数组+链表”,这种方式被称为“拉链法”。ConcurrentHashMap在这个 基本原理之上进行了各种优化。

首先是所有数据都放在一个大的HashMap中;其次是引入了红黑树。

如果头节点是Node类型,则尾随它的就是一个普通的链表;如果头节点是TreeNode类型,它的后 面就是一棵红黑树,TreeNode是Node的子类。

链表和红黑树之间可以相互转换:初始的时候是链表,当链表中的元素超过某个阈值时,把链表转 换成红黑树;反之,当红黑树中的元素个数小于某个阈值时,再转换为链表。

那为什么要做这种设计呢?

1. 使用红黑树,当一个槽里有很多元素时,其查询和更新速度会比链表快很多,Hash冲突的问 题由此得到较好的解决。

2. 加锁的粒度,并非整个ConcurrentHashMap,而是对每个头节点分别加锁,即并发度,就是 Node数组的长度,初始长度为16。

3. 并发扩容,这是难度最大的。当一个线程要扩容Node数组的时候,其他线程还要读写,因此 处理过程很复杂,后面会详细分析。

由上述对比可以总结出来:这种设计一方面降低了Hash冲突,另一方面也提升了并发度。

三、ConcurrentSkipListMap/Set

ConcurrentHashMap 是一种 key 无序的 HashMap,ConcurrentSkipListMap则是 key 有序的, 实现了NavigableMap接口,此接口又继承了SortedMap接口。

1、ConcurrentSkipListMap

为什么要使用SkipList实现Map?

在Java的util包中,有一个非线程安全的HashMap,也就是TreeMap,是key有序的,基于红黑树实 现。 而在Concurrent包中,提供的key有序的HashMap,也就是ConcurrentSkipListMap,是基于 SkipList(跳查表)来实现的。

这里为什么不用红黑树,而用跳查表来实现呢?

也就是目前计算机领域还未找到一种高效的、作用在树上的、无锁的、增加和删除节点的办法。

2、ConcurrentSkipListSet

ConcurrentSkipListSet只是对ConcurrentSkipListMap的简单封装。

责任编辑:admin

标签:

琼ICP备2022009675号-32

关于我们 联系邮箱:435 227 67@qq.com