先来说HashMap
HashMap实现原理
开始时数组加链表
对象的hashCode 会对应数组的一个元素
初始容量 DEFAULT_INITIAL_CAPACITY = 1 << 4
加载因子 DEFAULT_LOAD_FACTOR = 0.75f;
扩容阈值 threshold ==>超过就会扩容
在构造函数中 判断了初始化量的合理性
在put 的时候inflatetable()
hash 散列的位扰动
头部节点插入法
扩容的过程的循环,有死循环产生的
java7 之后hashMap 是使用红黑树实现 节点中超过8个元素就会改用红黑树
首先,我这里强调,ConcurrentHashMap的设计实现其实一直在演化,比如在Java8中就发生了非常大的变化(Java7其实也有不少更新),所以,我这里将比较分析结构、实现机制等方面,对比不同版本的主要区别。
早期ConcurrentHashMap,其实现是基于:
分离锁,也就是将内部进行分段(Segment),里面则是HashEntry的数组,和HashMap类似,哈希相同的条目也是以链表形式存放。
HashEntry内部使用volatile的value字段来保证可见性,也利用了不可变对象的机制以改进利用Unsafe提供的底层能力,比如volatileaccess,去直接完成部分操作,以最优化性能,毕竟Unsafe中的很多操作都是JVMintrinsic优化过的。
https://blog.csdn.net/qq_35452961/article/details/99753827
concurrentHashMap记录JAVA bug 的地方?
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6423457
在构造的时候,Segment的数量由所谓的concurrentcyLevel决定,默认是16,也可以在相应构造函数直接指定。注意,Java需要它是2的幂数值,如果输入是类似15这种非幂
值,会被自动调整到16之类2的幂数值。
ConcurrentHashMap会获取再入锁,以保证数据一致性,Segment本身就是基于ReentrantLock的扩展实现,所以,在并发修改期间,相应Segment是被锁定的。
在最初阶段,进行重复性的扫描,以确定相应key值是否已经在数组里面,进而决定是更新还是放置操作,你可以在代码里看到相应的注释。重复扫描、检测冲突
是ConcurrentHashMap的常见技巧。
下面我来对比一下,在Java 8和之后的版本中,ConcurrentHashMap发生了哪些变化呢?
总体结构上,它的内部存储变得和我在专栏上一讲介绍的HashMap结构非常相似,同样是大的桶(bucket)数组,然后内部也是一个个所谓的链表结构(bin),同步的粒度要
更细致一些。
其内部仍然有Segment定义,但仅仅是为了保证序列化时的兼容性而已,不再有任何结构上的用处。
因为不再使用Segment,初始化操作大大简化,修改为lazy-load形式,这样可以有效避免初始开销,解决了老版本很多人抱怨的这一点。
数据存储利用volatile来保证可见性。
使用CAS等操作,在特定场景进行无锁并发操作。
使用Unsafe、LongAdder之类底层手段,进行极端情况的优化。
网友评论