网站自建设需要买什么,ui设计包括哪些场景,定制开发小程序报价,西安大雁塔景点介绍ConcurrentHashMap集合学习
一、JDK1.7 和 1.8 版本ConcurrenHashMap对比分析
JDK 1.7版本
在JDK 1.7版本ConcurrentHashMap使用了分段锁的方式#xff08;对Segment进行加锁#xff09;#xff0c;其实际结构为#xff1a;Segment数组 HashEntry数组 链表。由很多个 …ConcurrentHashMap集合学习
一、JDK1.7 和 1.8 版本ConcurrenHashMap对比分析
JDK 1.7版本
在JDK 1.7版本ConcurrentHashMap使用了分段锁的方式对Segment进行加锁其实际结构为Segment数组 HashEntry数组 链表。由很多个 Segment 组合而每一个 Segment 是一个类似于 HashMap 的结构其可以进行扩容。但是 Segment 的个数一旦初始化就不能改变默认 Segment 的个数是 16 个你也可以认为 ConcurrentHashMap 默认支持最多 16 个并发线程。 Segment 继承了 ReentrantLock所以 Segment 内部可以很方便的获取锁
JDK 1.8版本
JDK1.8版本 中的 ConcurrentHashMap 使用的 Synchronized 锁 CAS 的机制。结构也由 Java7 中的 Segment 数组 HashEntry 数组 链表 进化成了 Node 数组 链表 / 红黑树Node 是类似于一个 HashMap 的结构。它的冲突再达到一定大小时会转化成红黑树在冲突小于一定数量时又退回链表。
Java 1.8 在链表长度超过一定阈值 8 时将链表寻址时间复杂度为 O(N)转换为红黑树寻址时间复杂度为 O(log(N))。Java 8 中锁粒度更细synchronized 只锁定当前链表或红黑二叉树的首节点这样只要 hash 不冲突就不会产生并发就不会影响其他 Node 的读写效率大幅提升。 二、JDK 1.8版本源码分析
一put 方法
根据 key 计算出 hashcode 。便于之后进一步确定Node的位置。判断是否需要进行初始化。 ConcurrentHashMap 的初始化是通过自旋和 CAS 操作完成的。即为当前 key 定位出的Node如果为空表示当前位置可以写入数据利用 CAS 尝试写入失败则自旋保证成功。如果当前位置的 hashcode MOVED -1,则需要进行扩容。如果都不满足则利synchronized 锁写入数据。synchronized锁当前Node节点相比与JDK1.7版本的segment分段锁锁粒度小如果数量大于一定长度TREEIFY_THRESHOLD 则要执行树化方法在 treeifyBin 中会首先判断当前数组长度 ≥64时才会将链表转换为红黑树。
public V put(K key, V value) {return putVal(key, value, false);
}/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {// key 和 value 不能为空if (key null || value null) throw new NullPointerException();int hash spread(key.hashCode());int binCount 0;for (NodeK,V[] tab table;;) {// f 目标位置元素NodeK,V f; int n, i, fh;// fh 后面存放目标位置的元素 hash 值if (tab null || (n tab.length) 0)// 数组桶为空初始化数组桶自旋CAS)tab initTable();else if ((f tabAt(tab, i (n - 1) hash)) null) {// 桶内为空CAS 放入不加锁成功了就直接 break 跳出if (casTabAt(tab, i, null,new NodeK,V(hash, key, value, null)))break; // no lock when adding to empty bin}else if ((fh f.hash) MOVED)tab helpTransfer(tab, f);else {V oldVal null;// 使用 synchronized 加锁加入节点synchronized (f) {if (tabAt(tab, i) f) {// 说明是链表if (fh 0) {binCount 1;// 循环加入新的或者覆盖节点for (NodeK,V e f;; binCount) {K ek;if (e.hash hash ((ek e.key) key ||(ek ! null key.equals(ek)))) {oldVal e.val;if (!onlyIfAbsent)e.val value;break;}NodeK,V pred e;if ((e e.next) null) {pred.next new NodeK,V(hash, key,value, null);break;}}}else if (f instanceof TreeBin) {// 红黑树NodeK,V p;binCount 2;if ((p ((TreeBinK,V)f).putTreeVal(hash, key,value)) ! null) {oldVal p.val;if (!onlyIfAbsent)p.val value;}}}}if (binCount ! 0) {if (binCount TREEIFY_THRESHOLD)treeifyBin(tab, i);if (oldVal ! null)return oldVal;break;}}}addCount(1L, binCount);return null;
}
二get方法
总结一下 get 过程
根据 hash 值计算位置。查找到指定位置如果头节点就是要找的直接返回它的 value。如果头节点 hash 值小于 0说明正在扩容或者是红黑树查找之。如果是链表遍历查找之。
public V get(Object key) {NodeK,V[] tab; NodeK,V e, p; int n, eh; K ek;// key 所在的 hash 位置int h spread(key.hashCode());if ((tab table) ! null (n tab.length) 0 (e tabAt(tab, (n - 1) h)) ! null) {// 如果指定位置元素存在头结点hash值相同if ((eh e.hash) h) {if ((ek e.key) key || (ek ! null key.equals(ek)))// key hash 值相等key值相同直接返回元素 valuereturn e.val;}else if (eh 0)// 头结点hash值小于0说明正在扩容或者是红黑树find查找return (p e.find(h, key)) ! null ? p.val : null;while ((e e.next) ! null) {// 是链表遍历查找if (e.hash h ((ek e.key) key || (ek ! null key.equals(ek))))return e.val;}}return null;
}
三、JDK 1.7 和 JDK 1.8 的 ConcurrentHashMap 实现有什么不同
线程安全实现方式JDK 1.7 采用 Segment 分段锁来保证安全 Segment 是继承自 ReentrantLock。JDK1.8 放弃了 Segment 分段锁的设计采用 Node CAS synchronized 保证线程安全锁粒度更细synchronized 只锁定当前链表或红黑二叉树的首节点。Hash 碰撞解决方法 : JDK 1.7 采用拉链法JDK1.8 采用拉链法结合红黑树链表长度超过一定阈值时将链表转换为红黑树。并发度JDK 1.7 最大并发度是 Segment 的个数默认是 16。JDK 1.8 最大并发度是 Node 数组的大小并发度更大。