hashmap 在1.7和1.8中的区别?concurrenthashmap
hashmap 在1.7和1.8中的区别?#
1.8 中concurrenthashmap的知识点#
重要属性–sizeCtl
负数代表正在进行初始化或扩容操作
-1代表正在初始化 -N表示有N-1个线程正在进行扩容操作 正数或者0代表hash表还没有被初始化,这个数值代表初始化或下一次进行扩容的大小,
一些属性
- 利用volatile方法设置节点位置的值
- 利用cas算法设置i节点位置上的node节点,如果相等才接受操作
- 如果当前线程中的值不是最新的值,这种修改可能会覆盖掉其他其他线程的修改结果
forwardingNode
用于连接两个table 的节点类,包含一个nextTable指针,用于指向下一张表,这个节点的k、v、next指针全部为null,它的hash值为-1
扩容方法transfer:
整个扩容分为两部分:
- 构建一个nexttable,容量2倍,单线程完成(RESIZE_STAMP_SHIFT)。
- 将原来的table中的元素复制到nexttable中,这里是可以多线程操作的。
单线程操作:
- 首先利用tabAt方法获得i位置的元素
- 位置为空,就在table中的i位置放入forwardNode节点
- 如果该位置是node节点(fh>=0),如果是头节点,构造一个反序链表,把处理结果分别放在nexttable的i和i+n的位置上。
- 如果该位置是treebin节点(fh<0),也做一个反序处理,并判断是否要untreefi(扩容后已经不再需要tree结构,反向转换为链表结构),把处理结果分别放在nexttable的i和i+n的位置上。
- 遍历过所有的节点以后完成了复制工作,这是让nexttable 作为新的table,并且更新sizeCtl为新容量的0.75倍,完成扩容
多线程操作:
- 如果遍历到的节点是forward节点,就向后遍历,再加上给节点上锁的机制,就完成了多线程的控制,多线程遍历节点,处理了一个节点,就把对应点的值set为forward,另一个线程看到forward ,就向后遍历,这样交叉就完成了复制工作。
helptransfer
当这个方法被调用时,当前concurrenthashmap已经有了nexttable对象,首先拿到nexttable对象,调用transfer方法可以看到,当本线程进入到扩容方法的时候会直接进入到复制阶段。
put方法
如果是树节点,按照树的方式插入,如果是链表则插入到末尾
get方法
必须满足两个条件:key相同 hash值相同
1.7中 concurrenthashmap知识点
运行时通过将key的高n位(n = 32 – segmentShift)和并发度减1(segmentMask)做位与运算定位到所在的Segment。segmentShift与segmentMask都是在构造过程中根据concurrency level被相应的计算出来。
get操作
- 根据key计算hash值
- 根据计算的hash值定位segment,如果segment不为null且segment.table也不为null,跳转进里面的循环
put操作
- 判断value是否为null
- 计算hash值
- 定位segment 如果不存在,就创建
- 调用segment的put方法:
1、获取锁,保证线程安全
2、定位到具体的HashEntry
3、遍历HashEntry链表,如果key已存在,再判断传入的onlyIfAbsent的值,再决定是否覆盖旧值
最后释放锁,返回旧值
size操作
第一次不加锁,计算更新的次数,第二次计算不加锁,重新记录更新数,比较两次的更新数,如果不一样,则进行第三次计算,第二次的更新数和第三次的比较,如果一样则返回count值,如果不同则对所有segment加锁进行计算
注意点
- concurrenthashmap中的key和value都不能为null,hashmap中key可以为null,hashtable中key不能为null
- concurrenthashmap是线程安全的类并不能保证concurrenthashmap的操作都是线程安全的
- concurrenthashmap的get操作不需要加锁,put操作需要加锁
- hash操作质量很高 ,如果hash后都存放在同一个segment中,那么使用这个类的意义就不会很大
1.7和1.8中ConcurrentHashMap的区别
1.7中concurrenthashmap主要使用Segment来减小锁力度,把hashmap分割成若干个segment,在put的时候需要锁住segment,get时候不加锁,使用volatile来保证可见性,当要统计全局时(size),首先会尝试多次计算modcount来确定,这几次尝试中,是否有其他线程进行了修改操作,如果没有,则直接返回size,如果有,则需要一次锁住所有的segment来计算。
1.7中concurrenthashmap中,当长度过长时碰撞会很频繁,链表的增删改查操作都会消耗很长的时间,所以1.8中完全重写了,主要有几点东西不同:
- 不采用segment而采用node,锁住node来实现减小锁力度
- 设计了moved状态,当resize的过程中,线程2还在put数据,线程2会帮助resize
- 使用三个cas操作来确保node的一些操作的原子性,这种方式代替了锁
- sizeCtl的不同值来代表不同含义,起到了控制的作用
concurrenthashmap和hashmap的区别?
hashmap 1.7 和 1.8
阅读更多- HashMap 在 Java1.7 与 1.8 中的区别
- HashMap在Java1.7与1.8中的区别
- HashMap在Java1.7与1.8中的区别
- HashMap在Java1.7与1.8中的区别
- 一文读懂JDK1.7,JDK1.8,JDK1.9的hashmap,hashtable,concurrenthashmap及他们的区别
- HashMap在Java1.7与1.8中的区别
- HashMap链表在Java1.7与1.8中的区别
- jdk源码剖析四:JDK1.7升级1.8 HashMap原理的变化
- jdk1.6 的 HashMap 源码分析及1.7,1.8的主要更改
- 牛客网Java刷题知识点之HashMap的实现原理、HashMap的存储结构、HashMap在JDK1.6、JDK1.7、JDK1.8之间的差异以及带来的性能影响
- 【不做标题党,只做纯干货】HashMap在jdk1.7和1.8中的实现
- Java 1.5, 1.6, 1.7, 1.8的区别
- jdk源码剖析四:JDK1.7升级1.8 HashMap原理的变化
- str.split(&amp;amp;amp;quot;&amp;amp;amp;quot;)在JDK1.7和JDK1.8中的区别
- Java集合,HashMap底层实现和原理(1.7数组+链表与1.8+的数组+链表+红黑树)
- jdk1.7和jdk1.8的区别
- JDK1.8、JDK1.7、JDK1.6区别看这里
- 【转】ConcurrentHashMap原理分析(1.7与1.8)
- jdk1.7和jdk1.8中hashmap区别