标签:next 问题 treenode 一个 2.3 resize 源码 结构 重要
1. 带着问题去阅读
为什么说ConcurrentHashMap是线程安全的?或者说 ConcurrentHashMap是如何防止并发的?
2. 字段和常量
首先,来看一下ConcurrentHashMap中的一些字段和常量,这些在接下来的操作中会用得到
2.1. 常量
从中,我们可以获得以下信息:
2.2. 字段
从这些字段中,我们可以获得以下信息:
2.3. 内部类
对比1.7里面的HashMap不难发现:
可以看到,TreeNode继承自Node,主要用于树形结构中。也就是说,TreeNode表示树中的结点。
还有一个TreeBin也是继承自Node
TreeBin表示整个树,TreeNode表示树中的结点
正常情况下,数组中某个位置的元素应该是Node,而Node是一个链表,它后面可能跟了多个Node。
但是,某个位置的节点个数超过阈值(默认8)时,将这个链表转成红黑树,那么此后数组中这个位置的元素就是TreeBin
也就是说,Node表示链表中的节点,TreeNode表示树中的节点,TreeBin表示树
3. 操作
3.1. put
这里,再多看一眼,刚才的putTreeVal()方法
总的来说,是先插入,后调整
大致流程是这样的:
3.2. resize
在上一步的put操作中,如果数组正在扩容,则帮助扩容
下面看一下扩容
我以前在理解上一直有一个误区,以前我一直以为在数组相同位置上的元素的哈希值都相同,今天我恍然大悟,原来不是这样的,这些元素之所以会在同一个位置是因为通过key的哈希值再结合数组长度计算得出该元素应该在这个位置上,而不同的哈希值可能经过计算也在同一个位置,所以,相同位置的元素的hash值不一定相同,或者说,链表上的元素的hash并不一定都相同,只是恰巧它们在数组的位置相同而已。
扩容是这样的:
下面是一个示意图,不必拘泥细节,重在意思
3.3. get和remove
删除和获取相对比较简单,不再赘述
至此,可以回答开头我们提出的问题了
sychronized + volatile + CAS
插入、删除、扩容的时候都对数组中相应位置的元素加锁了,加锁用的是synchronized
table数组、Node中的val和next、以及一些控制字段都加了volatile
在更新一些关键变量的时候用到了sun.misc.Unsafe中的一些方法
标签:next 问题 treenode 一个 2.3 resize 源码 结构 重要
原文地址:https://www.cnblogs.com/cjsblog/p/10017911.html