转载:http://blog.csdn.net/zhuqiuhui/article/details/51849692

今天研读Java并发容器和框架时,看到为什么要使用ConcurrentHashMap时,其中有一个原因是:线程不安全的HashMap, HashMap在并发执行put操作时会引起死循环,是因为多线程会导致HashMap的Entry链表形成环形数据结构,查找时会陷入死循环。纠起原因看了其他的博客,都比较抽象,所以这里以图形的方式展示一下,希望支持!

(1)当往HashMap中添加元素时,会引起HashMap容器的扩容,原理不再解释,直接附源代码,如下:

  1. /**
  2. *
  3. * 往表中添加元素,如果插入元素之后,表长度不够,便会调用resize方法扩容
  4. */
  5. void addEntry(int hash, K key, V value, int bucketIndex) {
  6. Entry<K,V> e = table[bucketIndex];
  7. table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
  8. if (size++ >= threshold)
  9. resize(2 * table.length);
  10. }
  11. /**
  12. * resize()方法如下,重要的是transfer方法,把旧表中的元素添加到新表中
  13. */
  14. void resize(int newCapacity) {
  15. Entry[] oldTable = table;
  16. int oldCapacity = oldTable.length;
  17. if (oldCapacity == MAXIMUM_CAPACITY) {
  18. threshold = Integer.MAX_VALUE;
  19. return;
  20. }
  21. Entry[] newTable = new Entry[newCapacity];
  22. transfer(newTable);
  23. table = newTable;
  24. threshold = (int)(newCapacity * loadFactor);
  25. }

(2)参考上面的代码,便引入到了transfer方法,(引入重点)这就是HashMap并发时,会引起死循环的根本原因所在,下面结合transfer的源代码,说明一下产生死循环的原理,先列transfer代码(这是里JDK7的源偌),如下:

  1. /**
  2. * Transfers all entries from current table to newTable.
  3. */
  4. void transfer(Entry[] newTable, boolean rehash) {
  5. int newCapacity = newTable.length;
  6. for (Entry<K,V> e : table) {
  7. while(null != e) {
  8. Entry<K,V> next = e.next;            ---------------------(1)
  9. if (rehash) {
  10. e.hash = null == e.key ? 0 : hash(e.key);
  11. }
  12. int i = indexFor(e.hash, newCapacity);
  13. e.next = newTable[i];
  14. newTable[i] = e;
  15. e = next;
  16. } // while
  17. }
  18. }

(3)假设:

  1. Map<Integer> map = new HashMap<Integer>(2);  // 只能放置两个元素,其中的threshold为1(表中只填充一个元素时),即插入元素为1时就扩容(由addEntry方法中得知)
  2. //放置2个元素 3 和 7,若要再放置元素8(经hash映射后不等于1)时,会引起扩容

假设放置结果图如下:

现在有两个线程A和B,都要执行put操作,即向表中添加元素,即线程A和线程B都会看到上面图的状态快照

执行顺序如下:

执行一:  线程A执行到transfer函数中(1)处挂起(transfer函数代码中有标注)。此时在线程A的栈中

  1. e = 3
  2. next = 7

执行二:线程B执行 transfer函数中的while循环,即会把原来的table变成新一table(线程B自己的栈中),再写入到内存中。如下图(假设两个元素在新的hash函数下也会映射到同一个位置)

执行三: 线程A解挂,接着执行(看到的仍是旧表),即从transfer代码(1)处接着执行,当前的 e = 3, next = 7, 上面已经描述。

1. 处理元素 3 , 将 3 放入 线程A自己栈的新table中(新table是处于线程A自己栈中,是线程私有的,不肥线程2的影响),处理3后的图如下:

2.  线程A再复制元素 7 ,当前 e = 7 ,而next值由于线程 B 修改了它的引用,所以next 为 3 ,处理后的新表如下图

3. 由于上面取到的next = 3, 接着while循环,即当前处理的结点为3, next就为null ,退出while循环,执行完while循环后,新表中的内容如下图:

4. 当操作完成,执行查找时,会陷入死循环!

并发的HashMap为什么会引起死循环?的更多相关文章

  1. 并发的HashMap为什么会引起死循环?(转)

    本文转自http://blog.csdn.net/zhuqiuhui/article/details/51849692 今天研读Java并发容器和框架时,看到为什么要使用ConcurrentHashM ...

  2. java常见面试问题总结

    JDK1.7 并发的HashMap为什么会引起死循环? hashmap如何解决hash冲突,为什么hashmap中的链表需要转成红黑树? hashmap什么时候会触发扩容? jdk1.8之前并发操作h ...

  3. HashMap并发导致死循环 CurrentHashMap

    为何出现死循环简要说明 HashMap闭环的详细原因 cocurrentHashMap的底层机制 为何出现死循环简要说明 HashMap是非线程安全的,在并发场景中如果不保持足够的同步,就有可能在执行 ...

  4. 并发HashMap的put操作引起死循环

    今天研读Java并发容器和框架时,看到为什么要使用ConcurrentHashMap时,其中有一个原因是:线程不安全的HashMap, HashMap在并发执行put操作时会引起死循环,是因为多线程会 ...

  5. Hashmap jdk7 死循环

    如果理解的有问题,欢迎大家指正. https://www.cnblogs.com/webglcn/p/10587708.html jdk7的hashmap 由数组和链表组成,存在几个问题: 当key的 ...

  6. hashmap引起死循环

    今天开发环境压测的时候出现cpu用满了情况,看线程堆栈,一堆线程都停留在org.apache.commons.collections4.map.AbstractHashedMap.put(Abstra ...

  7. HashMap闭环(死循环)的详细原因(转)

    为何出现死循环简要说明 HashMap是非线程安全的,在并发场景中如果不保持足够的同步,就有可能在执行HashMap.get时进入死循环,将CPU的消耗到100%. HashMap采用链表解决Hash ...

  8. [Java集合] 彻底搞懂HashMap,HashTable,ConcurrentHashMap之关联.

    注: 今天看到的一篇讲hashMap,hashTable,concurrentHashMap很透彻的一篇文章, 感谢原作者的分享. 原文地址: http://blog.csdn.net/zhanger ...

  9. [转]不正当使用HashMap导致cpu 100%的问题追究

    以前项目中遇到类似业务,但使用的是CurrentHashMap,看到这篇文章,转载记录,警示自己. 以下内容转自: 转载自并发编程网 – ifeve.com(http://ifeve.com/hash ...

随机推荐

  1. [PHP] swoole的安装和简单使用

    1. pecl install swoole 开启http2支持需要的依赖库:apt-get install nghttp2 开启的几个参数: enable sockets supports? [no ...

  2. [android] smartimageview&常见的开源代码

    github上搜索开源框架android-smarty-imageview,下载压缩包,拷贝我们之前写的网络图片查看器布局. 解压下载包里面的数据,找到java源码拷贝到我们的项目里,这时我们可以看到 ...

  3. 【Java每日一题】20170306

    20170303问题解析请点击今日问题下方的“[Java每日一题]20170306”查看(问题解析在公众号首发,公众号ID:weknow619) package Mar2017; public cla ...

  4. MyBatis进阶使用——动态SQL

    MyBatis的强大特性之一就是它的动态SQL.如果你有使用JDBC或者其他类似框架的经验,你一定会体会到根据不同条件拼接SQL语句的痛苦.然而利用动态SQL这一特性可以彻底摆脱这一痛苦 MyBati ...

  5. JavaScript 延迟加载

    默认情况下,浏览器是同步加载 JavaScript 脚本,即渲染引擎遇到<script>标签就会停下来,等到执行完脚本,再继续向下渲染.如果是外部脚本,还必须加入脚本下载的时间. 如果脚本 ...

  6. HTML5标签选择,图文混排使用dl dt dd

    图文混排,可以使用 dl dt dd(dd 和 dt 是同级,不可以嵌套,没有先后顺序) 1,上面红色部分是标题,可以使用h1里面包含一个span标签,样式一样,所以两个可以一起写. 2,上面黑色部分 ...

  7. 文件权限解释rwx

    linux文件权限 r 读w 写x (execute)执行 rwxrw-r-- 解释:rwx文件所有者对该文件有读写执行的权限:rw- 组内用户对该文件有读写的权限:r-- 其他非本组用户对该文件具有 ...

  8. laravel使用Schema创建数据表

    1.简介 迁移就像数据库的版本控制,允许团队简单轻松的编辑并共享应用的数据库表结构,迁移通常和Laravel的schema构建器结对从而可以很容易地构建应用的数据库表结构.如果你曾经告知小组成员需要手 ...

  9. css 性能优化小结

    1.能用css不用js 1.鼠标悬浮展示              2.自定义radio.checkbox的样式 3.巧用css伪类 当input获取焦点时候,把右边的按钮改变样式 检测用户输入:如果 ...

  10. thinkphp 使用paginate分页搜索带参数

    最近做项目发现使用paginate分页,搜索的时候点下一页搜索条件就变没了,所以在网上找了找一些方法,有的说是使用Page类,但是用习惯了paginate,再用Page不习惯,找到了一个方法,可以使用 ...