HashMap源码解析JDK1.7版本(四)

HashMap的常规知识点总结

为什么HashMap采用数组加链表的数据结构

采用数组是因为数组检索的时间复杂度是O(1),检索效率高。引入链表是为了解决hash冲突。

hash冲突的解决方案有哪些

rehash

引入链表

建立公共溢出区域

开放定址法:线性探测再散列、二次(平方)探测再散列、伪随机探测再散列

HashMap的扩容机制

  数组容量大于临界值同时插入的槽位上存在数据时,数组才会发生扩容。扩容后数组的大小是原先大小的2倍,最大为2的30次方。

HashMap不安全的原因

多线程put并发的时候可能造成数据的丢失

 我们假设三个线程同时运行,每个线程往HashMap里面插入一个KV键值对,分别为KV1、KV2、KV3。假设KV1、KV2、KV3中key的equals()方法判断相等且hash值一致,即它们都插入到数组的同一个槽位中。线程1的KV1正常插入,然后线程2的KV2执行完上图的位置1时,失去了cpu的执行权限,发生了阻塞,然后线程3的KV3在线程2阻塞时,也执行完了位置1处。此时,线程2和线程3中,变量e指向的都是线程1中插入的KV1。线程2和线程3继续往下执行,如果是线程2先执行完成,线程3后执行完成,那么线程3的KV3将会覆盖掉线程2的KV2;如果是线程3先执行完成,线程2后执行完成,那么线程2的KV2将会覆盖掉线程3的KV3。此时就会导致数据的丢失!!!

多线程put和get并发的时候,可能导致get为null或取到的元素不对

put放入元素时,如果达到临界值且插入的槽位已有元素,就会发生扩容,数组的扩容就伴随着rehash,rehash就会导致Entry的槽位可能发生变化。当多线程并发时,线程1调用put方法插入元素,线程2调用get方法获取元素。线程2计算完key的hash值,确定了待获取的元素的在数组的索引,但是还没有获取到数据。此时,线程1插入元素时,触发了数组扩容,所有的元素都进行了rehash,原先线程2想要获取的元素经过rehash后,放到了数组的另一个槽位中,那么,线程2在数组扩容后,再去原先的数组索引处取元素,就可能会取到null值或者是另外一个非预期的元素。

并发扩容

问题1:JDK1.7数组扩容时,元素的移动是采用的头插法,可能会产生循环引用,导致内存泄漏,cpu使用率飙升。

 假设数组同一个槽位上有a、b、c三个元素且a指向b,b指向c,形成链表结构,假设abc三个元素扩容后还是存储在同一个槽位。多线程并发时,线程1执行到Entry<K,V> next = e.next;完成赋值后,短暂阻塞,此时线程1中e为a元素,e.next和局部变量next为b元素线程2正常执行完扩容操作,此时table里面元素为c指向b,b指向a。线程2完成扩容后,因为数组中的元素是线程共享的,所以线程1此时元素的状态也是c指向b,b指向a线程1继续执行,根据线程1阻塞前的赋值情况,第一次while循环为a元素,a元素的next节点赋值为null,然后把数组槽位上的值设置为a元素;根据线程1阻塞前的赋值情况,第二次循环为b元素,此时b元素的next指向的元素为a,所以下一次循环为a元素,然后把b的next节点设置为数组槽位上的值,即b的next节点指向a元素,然后把数组槽位上的值设置为b元素第三次循环为a元素a元素的next节点为null,所以执行结束会跳出while循环,然后把a的next节点设置为数组槽位上的值,即a的next节点指向b元素,然后把数组槽位上的值设置为a元素。

  循环结束,此时a元素指向b元素,b元素指向a元素,循环引用。当查询HashMap中的元素时,如果查询的元素既不是a也不是b,一旦遍历到a元素和b元素所在的槽位,就会形成死循环。

问题2:线程1和线程2在插入新元素时,都判断需要进行扩容,但是线程2判断完需要扩容后,进行了短暂的阻塞,等线程1扩容完成后,线程2继续进行。此时,线程2获取到的原数组就是线程1扩容后的数组,这样就会导致数组的容量再次扩大。但是这种情况并不是错误,只是会导致扩容后的数组比预期的大,浪费了存储空间而已。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值