https://www.cnblogs.com/study-everyday/p/6430462.html
JDK1.7的实现
- ConcurrentHashMap的数据结构是由一个Segment数组和多个HashEntry组成
- 每一个Segment元素存储的是HashEntry数组+链表,这个和HashMap的数据存储结构一样
- HashEntry大小的计算也是2的N次方(cap <<=1)
- Segment实现了ReentrantLock,也就带有锁的功能,
- 当执行put操作时,会进行第一次key的hash来定位Segment的位置
- 这里要进行两次Hash去定位数据的存储位置
- 当执行put操作时,会进行第一次key的hash来定位Segment的位置
- 计算ConcurrentHashMap的元素大小是一个有趣的问题
- 第一种方案他会使用不加锁的模式去尝试多次计算ConcurrentHashMap的size,最多三次,
- 比较前后两次计算的结果,结果一致就认为当前没有元素加入,计算的结果是准确的
- 第二种方案是如果第一种方案不符合,他就会给每个Segment加上锁,然后计算ConcurrentHashMap的size返回
- 第一种方案他会使用不加锁的模式去尝试多次计算ConcurrentHashMap的size,最多三次,
JDK1.8的实现
- 直接用Node数组+链表+红黑树的数据结构来实现,并发控制使用Synchronized和CAS来操作
- JDK1.8中还能看到Segment的数据结构,但是已经简化了属性,只是为了兼容旧版本
- 当链表的节点数大于8时会转换成红黑树的结构
- ConcurrentHashMap的初始化其实是一个空实现,并没有做任何事
- 在put操作中实现,当然ConcurrentHashMap还提供了其他的构造函数,
- 有指定容量大小或者指定负载因子,跟HashMap一样,这里就不做介绍了
- 扩容是并发扩容,
- 调用多个工作线程一起帮助进行扩容
- 锁加在treeMap的根节点
- 扩容需要整个数组扩容
- 一个数组位置(根节点所在位置)一个线程来执行
- 其他线程扩容后面的微信
- nextTable:默认为null,扩容时新生成的数组,其大小为原数组的两倍。
- 只有table发生扩容的时候,ForwardingNode才会发挥作用,
- 作为一个占位符放在table中表示当前节点为null或则已经被移动
来源:oschina
链接:https://my.oschina.net/u/3847203/blog/3051741