转载: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. Java垃圾回收器的工作原理

    上课,老师照本宣科,实在难以理解,干脆就看书包里的Java书,正好看了Java的垃圾回收器是如何工作的,觉得有必要记录一下. 参考于 Java编程思想第四版(Thinking in Java) 老年代 ...

  2. JavaScript初学者必看“箭头函数”

    译者按: 箭头函数看上去只是语法的变动,其实也影响了this的作用域. 原文: JavaScript: Arrow Functions for Beginners 译者: Fundebug 为了保证可 ...

  3. js 原生手写AJAX

    前言:最近在学习react,在练习中模拟一个button通过AJAX向后台发送POST请求,懒得引入AXIOS,就顺便练习了js原生ajax. 正文: 注:我忽略了IE6及以下版本 submit(){ ...

  4. 如何将字符串格式的对象转换成真正的js对象?

    1.如何将字符串格式的对象转换成真正的js对象? <script>//eval 的作用eval('var a = 100');console.log(a);</script> ...

  5. 7;XHTML表单

    1.表单的功能结构 2.文本栏.密码栏.隐藏栏 3.复选栏.单选栏 4.窗体栏位.区块栏位 5.按钮.图像按钮 6.允许上传文件 7.表单的外框和标题 8.元件的次序和快捷键 表单是提供让读者在网页上 ...

  6. java ee,包括js,html,jsp等等知识整合

    为了方便修改和后续的包装套路   首先用户访问的页面从web.xml找到 <welcome-file-list> <welcome-file>index.html</we ...

  7. js 分页插件(jQuery)

    参考:http://www.jb51.net/article/117191.htm 侵删 css 部分 @charset "utf=8"; *{ box-sizing: borde ...

  8. Vue2+VueRouter2+webpack 构建项目实战(二):目录以及文件结构

    通过上一篇博文<Vue2+VueRouter2+webpack 构建项目实战(一):准备工作>,我们已经新建好了一个基于vue+webpack的项目.本篇文章详细介绍下项目的结构. 项目目 ...

  9. 进入root提示Authentication failure错误

    新安装Ubuntu 18.4,想进入root角色,提示“Authentication failure” 失败. 因为是新安装,并无root的密码,所以需要新增加: sudo passwd root,之 ...

  10. .net core 下编码问题

    System.Globalization.CultureInfo.CurrentCulture = new System.Globalization.CultureInfo("zh-CN&q ...