以下内容来自网络整理,侵删
如何求size
- JDK 8 推荐使用mappingCount 方法,因为这个方法的返回值是 long 类型,不会因为 size 方法是 int 类型限制最大值
- 在没有并发的情况下,使用一个 volatile 修饰的baseCount 变量就足够了
- 当并发的时候,CAS 修改 baseCount 失败后,就会使用 CounterCell 类了,会创建一个这个对象,通常对象的 volatile value 属性是 1。在计算 size 的时候,会将 baseCount 和 CounterCell 数组中的元素的 value 累加,得到总的大小,但这个数字仍旧可能是不准确的。
数据结构
JDK1.7
- 整个 ConcurrentHashMap 由一个个 Segment 组成,Segment 代表”部分“或”一段“的意思,所以很多地方都会将其描述为分段锁。
- 我们可以把每个 Segment 看成是一个小的 HashMap,其内部结构和 HashMap 是一模一样的
JDK1.8
- 1.8中放弃了Segment分段锁的设计,使用的是Node数组+CAS+Synchronized来保证线程安全性
Put方法
- 篇幅有限,只介绍JDK1.8的
- 以下步骤重复循环执行,直到成功:
- 如果node数组没有初始化,则初始化
- 通过hash值找到当前 key 所在的桶,并且判断是否为空(冲突),如果为空,则通过CAS 原子操作,把新节点插入到此位置
- 如果数组正在进行扩容,则需要当前线程帮忙迁移数据
-如果存在hash冲突,就通过synchronized加锁来保证线程安全,这里有两种情况,一种是链表形式就直接遍历到尾端插入,一种是红黑树就按照红黑树结构插入 - 最后如果Hash冲突时会形成Node链表,在链表长度超过8会将链表结构转换为红黑树的结构
Get方法
- 篇幅有限,只介绍JDK1.8的
- 计算 hash 值
- 根据 hash 值找到数组对应位置: (n - 1) & h
- 根据该位置处结点性质进行相应查找
- 如果该位置为 null,那么直接返回 null 就可以了
- 如果该位置处的节点刚好就是我们需要的,返回该节点的值即可
- 如果该位置节点的 hash 值小于 0,说明正在扩容,或者是红黑树,后面我们再调用 find 方法
- 如果以上 3 条都不满足,那就是链表,进行遍历比对即可