您的位置:首页 > 其它

hashmap 在1.7和1.8中的区别?concurrenthashmap

2018-10-23 11:03 316 查看

hashmap 在1.7和1.8中的区别?#

1.8 中concurrenthashmap的知识点#

重要属性–sizeCtl

负数代表正在进行初始化或扩容操作
-1代表正在初始化 -N表示有N-1个线程正在进行扩容操作 正数或者0代表hash表还没有被初始化,这个数值代表初始化或下一次进行扩容的大小,

一些属性

  1. 利用volatile方法设置节点位置的值
  2. 利用cas算法设置i节点位置上的node节点,如果相等才接受操作
  3. 如果当前线程中的值不是最新的值,这种修改可能会覆盖掉其他其他线程的修改结果

forwardingNode

用于连接两个table 的节点类,包含一个nextTable指针,用于指向下一张表,这个节点的k、v、next指针全部为null,它的hash值为-1

扩容方法transfer:

整个扩容分为两部分:

  1. 构建一个nexttable,容量2倍,单线程完成(RESIZE_STAMP_SHIFT)。
  2. 将原来的table中的元素复制到nexttable中,这里是可以多线程操作的。

单线程操作:

  1. 首先利用tabAt方法获得i位置的元素
  2. 位置为空,就在table中的i位置放入forwardNode节点
  3. 如果该位置是node节点(fh>=0),如果是头节点,构造一个反序链表,把处理结果分别放在nexttable的i和i+n的位置上。
  4. 如果该位置是treebin节点(fh<0),也做一个反序处理,并判断是否要untreefi(扩容后已经不再需要tree结构,反向转换为链表结构),把处理结果分别放在nexttable的i和i+n的位置上。
  5. 遍历过所有的节点以后完成了复制工作,这是让nexttable 作为新的table,并且更新sizeCtl为新容量的0.75倍,完成扩容

多线程操作:

  1. 如果遍历到的节点是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操作

  1. 根据key计算hash值
  2. 根据计算的hash值定位segment,如果segment不为null且segment.table也不为null,跳转进里面的循环

put操作

  1. 判断value是否为null
  2. 计算hash值
  3. 定位segment 如果不存在,就创建
  4. 调用segment的put方法:
    1、获取锁,保证线程安全
    2、定位到具体的HashEntry
    3、遍历HashEntry链表,如果key已存在,再判断传入的onlyIfAbsent的值,再决定是否覆盖旧值
    最后释放锁,返回旧值

size操作

第一次不加锁,计算更新的次数,第二次计算不加锁,重新记录更新数,比较两次的更新数,如果不一样,则进行第三次计算,第二次的更新数和第三次的比较,如果一样则返回count值,如果不同则对所有segment加锁进行计算

注意点

  1. concurrenthashmap中的key和value都不能为null,hashmap中key可以为null,hashtable中key不能为null
  2. concurrenthashmap是线程安全的类并不能保证concurrenthashmap的操作都是线程安全的
  3. concurrenthashmap的get操作不需要加锁,put操作需要加锁
  4. 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中完全重写了,主要有几点东西不同:

  1. 不采用segment而采用node,锁住node来实现减小锁力度
  2. 设计了moved状态,当resize的过程中,线程2还在put数据,线程2会帮助resize
  3. 使用三个cas操作来确保node的一些操作的原子性,这种方式代替了锁
  4. sizeCtl的不同值来代表不同含义,起到了控制的作用

concurrenthashmap和hashmap的区别?

hashmap 1.7 和 1.8

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: