之前我已经写过关于HashMap的内容了:http://www.cnblogs.com/wang-meng/p/7545725.html
我们都知道HashMap是线程不安全的, 如果多线程来访问会有什么问题呢? 答案是会造成死锁。
接下来我们就分析下为何会造成死锁。
说到HashMap中死锁的情况, 我们就必须要先讲下resize()方法, 顾名思义, 这个方法就是来扩容的。
当HashMap的size超过 thredshold时, 就需要扩容了。 当我们put时:
(截图代码为JDK7 HashMap源码)
 
首先,我们需要知道几个最基本的概念: Entry<K,V>[] table的初始化长度length(默认值是16),Load factor为负载因子(默认值是0.75),threshold是HashMap所能容纳的最大数据量的Entry(键值对)个数。size是HashMap中实际存在 的键值对数量。threshold = length * Load factor。也就是说,在数组定义好长度之后,负载因子越大,所能容纳的键值对个数越多。
 
接着我们直接看上面的代码, 当size >= threshold且table[bucketIndex]不为空就会触发resize操作。 然后看resize()方法:
 
这里重点就是transfer方法, 接着我们来看transfer方法:
 
第一: 遍历旧的table
第二: 将旧的table中每个元素重新计算hash值, 然后赋予新的table中
具体我们将用图示的方法来解析:
 
单线程扩容:
假设:hash算法就是简单的key与length(数组长度)求余。
          hash表长度为2,如果不扩容, 那么元素key为3,5,7按照计算(key%table.length)的话都应该碰撞到table[1]上
         
扩容:hash表长度会扩容为4
          重新hash,key=3 会落到table[3]上(3%4=3), 当前e.next为key(7), 继续while循环
          重新hash,key=7 会落到table[3]上(7%4=3), 产生碰撞, 这里采用的是头插入法,所以key=7的Entry会排在key=3前面(这里可以具体看while语句中代码)
          当前e.next为key(5), 继续while循环
          重新hash,key=5 会落到table[1]上(5%4=1), 当前e.next为null, 跳出while循环, resize结束
         
 
如题如图所示:
 
 
 
多线程扩容:
这里我们先把核心代码搬出来, 方便查看

while(null != e) {
    Entry<K,V> next = e.next; //第一行
    int i = indexFor(e.hash, newCapacity); //第二行
    e.next = newTable[i]; //第三行
    newTable[i] = e; //第四行
    e = next; //第五行
}


去掉了一些冗余的代码, 层次结构更加清晰了。
第一行:记录odl hash表中e.next
第二行:rehash计算出数组的位置(hash表中桶的位置)
第三行:e要插入链表的头部, 所以要先将e.next指向new hash表中的第一个元素
第四行:将e放入到new hash表的头部
第五行: 转移e到下一个节点, 继续循环下去

核心代码如上所说, 下面就是多线程同时put的情况了, 然后同时进入transfer方法中:

假设这里有两个线程同时执行了put()操作,并进入了transfer()环节

while(null != e) {
Entry<K,V> next = e.next; //线程1执行到这里被调度挂起了
e.next = newTable[i];
newTable[i] = e;
e = next;
}

那么现在的状态为:

从上面的图我们可以看到,因为线程1的 e 指向了 key(3),而 next 指向了 key(7),在线程2 rehash 后,就指向了线程2 rehash 后的链表。

然后线程1被唤醒了:

  1. 执行e.next = newTable[i],于是 key(3)的 next 指向了线程1的新 Hash 表,因为新 Hash 表为空,所以e.next = null
  2. 执行newTable[i] = e,所以线程1的新 Hash 表第一个元素指向了线程2新 Hash 表的 key(3)。好了,e 处理完毕。
  3. 执行e = next,将 e 指向 next,所以新的 e 是 key(7)

然后该执行 key(3)的 next 节点 key(7)了:

  1. 现在的 e 节点是 key(7),首先执行Entry<K,V> next = e.next,那么 next 就是 key(3)了
  2. 执行e.next = newTable[i],于是key(7) 的 next 就成了 key(3)
  3. 执行newTable[i] = e,那么线程1的新 Hash 表第一个元素变成了 key(7)
  4. 执行e = next,将 e 指向 next,所以新的 e 是 key(3)

这时候的状态图为:

然后又该执行 key(7)的 next 节点 key(3)了:

  1. 现在的 e 节点是 key(3),首先执行Entry<K,V> next = e.next,那么 next 就是 null
  2. 执行e.next = newTable[i],于是key(3) 的 next 就成了 key(7)
  3. 执行newTable[i] = e,那么线程1的新 Hash 表第一个元素变成了 key(3)
  4. 执行e = next,将 e 指向 next,所以新的 e 是 key(7)

这时候的状态如图所示:

 
很明显,环形链表出现了!!当然,现在还没有事情,因为下一个节点是 null,所以transfer()就完成了,等put()的其余过程搞定后,HashMap 的底层实现就是线程1的新 Hash 表了。

 

HashMap中的resize以及死链的情况的更多相关文章

  1. Java HashMap中在resize()时候的rehash,即再哈希法的理解

    HashMap的扩容机制---resize() 虽然在hashmap的原理里面有这段,但是这个单独拿出来讲rehash或者resize()也是极好的. 什么时候扩容:当向容器添加元素的时候,会判断当前 ...

  2. Apache下通过shell脚本提交网站404死链

    网站运营人员对于死链这个概念一定不陌生,网站的一些数据删除或页面改版等都容易制造死链,影响用户体验不说,过多的死链还会影响到网站的整体权重或排名. 百度站长平台提供的死链提交工具,可将网站存在的死链( ...

  3. 利用shell脚本批量提交网站404死链给百度

    网站运营人员对于死链这个概念一定不陌生,网站的一些数据删除或页面改版等都容易制造死链,影响用户体验不说,过多的死链还会影响到网站的整体权重或排名. 百度站长平台提供的死链提交工具,可将网站存在的死链( ...

  4. 使用selenium的方式获取网页中图片的链接和网页的链接,来判断是否是死链(二)

    上一篇使用Java正则表达式来判断和获取图片的链接以及跳转的网址,这篇使用selenium的自带的API(getAttribute)来获取网页中指定的内容 实现内容:获取下面所有图片的链接地址以及跳转 ...

  5. java中避免集合死链调用

    目录 1. 前言 2. 场景 3. 环境 3.1 开发环境准备 3.2 数据准备 3.2.1 Mysql数据库表及数据 3.2.2 redis库数据 4. 解决方式 5.完整代码 5.1Model 5 ...

  6. [改善Java代码]减少HashMap中元素的数量

    在系统开发中我们经常会使用HashMap作为数据集容器,或者是用缓冲池来处理,一般很稳定,但偶尔也会出现内存溢出的问题(OutOfMemory错误),而且这经常是与HashMap有关的.而且这经常是与 ...

  7. Java 使用正则表达式取出图片地址以及跳转的链接地址,来判断死链(一)

    任务:通过driver的getPageSource()获取网页的源码内容,在把网页中图片链接地址和跳转的url地址进行过滤,在get每个请求,来判断是否是死链 如图: 获取网页源码中所有的href,以 ...

  8. 【java基础 12】HashMap中是如何形成环形链表的?

    导读:经过前面的博客总结,可以知道的是,HashMap是有一个一维数组和一个链表组成,从而得知,在解决冲突问题时,hashmap选择的是链地址法.为什么HashMap会用一个数组这链表组成,当时给出的 ...

  9. 关于红黑树,在HashMap中是怎么应用的?

    关于红黑树,在HashMap中是怎么应用的? 前言 在阅读HashMap源码时,会发现在HashMap中使用了红黑树,所以需要先了解什么是红黑树,以及其原理.从而再进一步阅读HashMap中的链表到红 ...

随机推荐

  1. scp的简单记忆方法

    scp虽然只有把文见发送到远端和从远端copy文件俩功能,但是常常把俩功能的先写什么给计混了,所以我就用通俗的大白话给总结了下,十分容易记忆,这里给大家分享一下.scp 我们常用的两个功能: (1)把 ...

  2. bzoj 3576[Hnoi2014]江南乐 sg函数+分块预处理

    3576: [Hnoi2014]江南乐 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 1929  Solved: 686[Submit][Status ...

  3. IOS 使用cocoapods后无法导入头文件问题

    IOS 使用cocoapods后无法导入头文件问题 这时候如果你发现import的时候没有提示AFN e t wo r k i n g.h的文件,可以在target-Build Settings下修改 ...

  4. Opencv 3.3.0 常用函数

    如何调图像的亮度和对比度? //如何增加图片的对比度或亮度? void contrastOrBrightAdjust(InputArray &src,OutputArray &dst, ...

  5. uva1343 IDA*

    这题需要用数组记录每个block的位置.启发函数:d+wa(8-当前最多相同个数)>maxd直接退出 AC代码: #include<cstdio> #include<cstri ...

  6. 四级地址插件升级改造(京东商城地址选择插件)city-picker

    最近公司做的项目要和京东的数据做对接,所以要做个类似京东商品的详情页.页面的数据,是可以从京东接口获取到的,但是地址插件选择的效果需要自己实现.前端的同事在之前的项目中,已经选择了一款地址插件(cit ...

  7. xp+WinDBG+VMware调试内核

    呵呵,搞点突兀的标题而已.其实说的还是如何使用WinDBG和VMware来搭建调试内核的环境而已,这些网上已经有数不清的教程了,不过我喜欢自己亲手写一下.第一,把这个过程写一遍能加深印象,就算以后忘记 ...

  8. 安装STS报错(三)

    安装STS报错 1.具体报错如下 Failure to transfer org.codehaus.plexus:plexus-archiver:jar:1.2 from http://repo.ma ...

  9. 利用Eclipse中的Maven构建Web项目(一)

    利用Eclipse中的Maven构建Web项目 1.新建一个Maven Project,"New-->Other..." 2.选择"Maven Project&qu ...

  10. jquery获取选中的文本和值

    jquery获取选中的文本和值 1.说明 (1)获取select下拉框选中的索引       $("#selection").get(0).selectedIndex; (2)获取 ...