- jdk7:
- 底层是使用segment分段锁 数据结构:数组+链表
- segment数组 segment[] 每一个segment对象都是继承了Reentrantlock,
- 每一个segment 下对应一个数组:hashEntry
[] table ,int count 元素个数,modcount 修改次数,
threshold阈值,loadFactor 加载因子 - put方法:对每一个segnment上锁 ,不影响修改其他的segment,volatile 修饰hashEntry
[] table ,可以保证在数组扩容时的可见性 - get方法不加锁,因为volatile修饰value 和 下一个节点 next,保证多线程下可见性
- 并发度: 默认是16 太小会带来严重的锁竞争问题,过大也会是原本访问一个segment内的元素,会扩散到 不同的segment ,从而影响性能
- jdk1.8
- 底层结构 使用 数组+链表+红黑树
- 红黑树查询的时间复杂度是O(logN),链表是O(n)
- 数组采用的是node数组 , 使用的是CAS+synchroniezd 保证线程安全
- 所得粒度:jdk7 是对需要机芯数据炒作的segment加锁,而jdk1.8是对每一个数组元素的头结点加锁.
- 并发度:取决于数组的长度
- jdk8中为什么使用synchronized 替换reentrantlock
- synchronized 性能提升,在jdk6中对synchronized 锁的实现 引入了大量的优化,会从无所->偏向锁->轻量级锁->重量级锁的一步步转换 ,就是锁膨胀的优化 ,以及有锁的粗化 ,锁消除,自适应自旋等优化
- 减少内存的开销:segment 要达到同样的效果 会大量的继承Reentrantlock 带来内存浪费
- 迭代器:
- ConcurrentHashMap是弱一致性的,在迭代过程中 ,如果改变了还没有迭代到的部分 ,会显示出来,因为value 使用了volatile 修饰 保证了可见性
- hashmap是强一致性的 迭代器初始化后 ,数据是不可以改变的 ,且使用原来的remove方法会使modcount++,导致迭代器中的修改次数espectcount 和modcount不相等,出现并发修改异常