再谈哈希表

1)jdk1.7中hashmap采用头插法为啥会出现死循环
关于这个问题,网上几乎都是一致结论,两个线程同时put,数组扩容时,线程A卡住了(卡在了newTable[i]=e这一行),线程B顺利执行完毕,最终线程A继续执行会导致最终形成循环链表;
我的疑问是线程A为啥会卡住,而且还要等线程B执行完毕?这样分析有意义吗?
如果是站在cpu时间片角度分析,应该更合理一些;
伪代码如下:
while(e != null){
next = e.next; //步骤1

…步骤1与步骤2之间的逻辑,是在计算e在newTable中的索引i,此处省略;

e.next=newTable[i]; //步骤2
newTable[i]=e; //步骤3
e=next; //步骤4
}
线程a执行完步骤1,cpu时间片用完,线程a处于就绪状态,线程b拿到cpu时间片执行完步骤3,此时在新表的桶位i存储了旧表的链表的首元素x,并且将该状态刷回主存;此时线程b的时间片用完,线程a获得cpu时间片继续执行,获取主存中新表状态,而且e依旧表示元素x,当执行完步骤2时,此时元素x指向了新表桶位i的数据,即指向了自己,导致死循环;
2)jdk1.7和1.8中的哈希表在编码方面
在编码方面,1.8中的hashmap代码通用型更强了,但没有1.7中的直观;如put方法,1.7中直接判断若key为null,则防置在table[0]处,但1.8中是先根据key为null得到hash为0,再在newNode方法中放在table[0]处,下边这行代码可以处理hash值是任何值的情况;代码通用型强,但需要绕进来,才知道是如何处理key我null的情况;jdk1.8处理如下所示;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);

但在jdk1.7中,直接在put方法中就可以看到若key为null,则调用putForNullKey方法;jdk1.7处理如下所示;
在这里插入图片描述

另外一个地方就是初始化table时,1.7直接调用单独的方法inflateTable,1.8中则调用扩容的resize方法;jdk1.8中处理如下图所示;resize方法通用型强,但既负责初始化也负责扩容,较inflateTable方法更复杂;

在这里插入图片描述

3)jdk1.7和jdk1.8的concurrentHashMap的一些区别
其实1.8也是分段锁定的思想,只是1.8中并发锁定粒度更小;比如1.7中默认是16张哈希表,多线程过来路由到不同的哈希表,每个线程会给所路由到的哈希表上锁进行put操作,每张哈希表数组初始大小为2(好像没记错);
1.8中就一张哈希表,初始化数组中的桶位时,通过cas保证只有一个线程能初始化,cas中就是基于sizeCtl字段进判断,如果发生哈希冲突,也是cas方式抢占头节点,如果桶位不为null,则加synchronized,所以只是在桶位不为null时采用独占锁,锁的粒度明显减小了;但我在思考,桶位不为null,甚至都可以不用synchronized,而用cas;看下这段33行的代码;其实只要f不同,多线程一样可以操作同一个数组的不同桶位;类似分段锁思想;看了这段,会发现并不是简单的判断链表的key相等与否问题,还涉及红黑树,如果不加锁,会出现什么问题呢
在这里插入图片描述
在这里插入图片描述

聊下jdk1.8的concurrentHashMap多线程扩容,针对的是多个线程操作同一个concurrentHashMap实例的put方法触发的扩容,有个关键参数transferIndex,该参数是实例变量,意味着此时多个线程共享该变量,通过该参数,就可以判断任务是否分配完成,transferIndex从length-1开始,当为0时表示数组迁移的任务已被分配完,没抢到任务的线程可以退出;而每个线程调用transfer方法,都有自己局部变量i,bound,控制自己的任务执行进度;
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

orcharddd_real

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值