1、多线程put操作后,get操作导致死循环。

2、多线程put非null元素后,get操作得到null值。

3、多线程put操作,导致元素丢失。

死循环场景重现

下面我用一段简单的DEMO模拟HashMap死循环:

 1 public class Test extends Thread
2 {
3 static HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(2);
4 static AtomicInteger at = new AtomicInteger();
5
6 public void run()
7 {
8 while(at.get() < 100000)
9 {
10 map.put(at.get(),at.get());
11 at.incrementAndGet();
12 }
13 }

其中map和at都是static的,即所有线程所共享的资源。接着5个线程并发操作该HashMap:

 1 public static void main(String[] args)
2 {
3 Test t0 = new Test();
4 Test t1 = new Test();
5 Test t2 = new Test();
6 Test t3 = new Test();
7 Test t4 = new Test();
8 t0.start();
9 t1.start();
10 t2.start();
11 t3.start();
12 t4.start();
13 }

反复执行几次,出现这种情况则表示死循环了:

接下来我们去查看下CPU以及堆栈情况:

通过堆栈可以看到:Thread-3由于HashMap的扩容操作导致了死循环。

正常的扩容过程

我们先来看下单线程情况下,正常的rehash过程

1、假设我们的hash算法是简单的key mod一下表的大小(即数组的长度)。

2、最上面是old hash表,其中HASH表的size=2,所以key=3,5,7在mod 2 以后都冲突在table[1]这个位置上了。

3、接下来HASH表扩容,resize=4,然后所有的<key,value>重新进行散列分布,过程如下:

在单线程情况下,一切看起来都很美妙,扩容过程也相当顺利。接下来看下并发情况下的扩容。

并发情况下的扩容

1、首先假设我们有两个线程,分别用红色和蓝色标注了。

2、扩容部分的源代码:

 1 void transfer(Entry[] newTable) {
2 Entry[] src = table;
3 int newCapacity = newTable.length;
4 for (int j = 0; j < src.length; j++) {
5 Entry<K,V> e = src[j];
6 if (e != null) {
7 src[j] = null;
8 do {
9 Entry<K,V> next = e.next;
10 int i = indexFor(e.hash, newCapacity);
11 e.next = newTable[i];
12 newTable[i] = e;
13 e = next;
14 } while (e != null);
15 }
16 }
17 }

3、如果在线程一执行到第9行代码就被CPU调度挂起,去执行线程2,且线程2把上面代码都执行完毕。我们来看看这个时候的状态:

4、接着CPU切换到线程一上来,执行8-14行代码,首先安置3这个Entry:

这里需要注意的是:线程二已经完成执行完成,现在table里面所有的Entry都是最新的,就是说7的next是3,3的next是null;现在第一次循环已经结束,3已经安置妥当。看看接下来会发生什么事情:

1、e=next=7;

2、e!=null,循环继续

3、next=e.next=3

4、e.next 7的next指向3

5、放置7这个Entry,现在如图所示:

放置7之后,接着运行代码:

1、e=next=3;

2、判断不为空,继续循环

3、next= e.next  这里也就是3的next 为null

4、e.next=7,就3的next指向7.

5、放置3这个Entry,此时的状态如图:

这个时候其实就出现了死循环了,3移动节点头的位置,指向7这个Entry;在这之前7的next同时也指向了3这个Entry。

代码接着往下执行,e=next=null,此时条件判断会终止循环。这次扩容结束了。但是后续如果有查询(无论是查询的迭代还是扩容),都会hang死在table【3】这个位置上。现在回过来看文章开头的那个Demo,就是挂死在扩容阶段的transfer这个方法上面。

出现上面这种情况绝不是我要在测试环境弄一批数据专门为了演示这种问题。我们仔细思考一下就会得出这样一个结论:如果扩容前相邻的两个Entry在扩容后还是分配到相同的table位置上,就会出现死循环的BUG。在复杂的生产环境中,这种情况尽管不常见,但是可能会碰到。

多线程put操作,导致元素丢失

下面来介绍下元素丢失的问题。这次我们选取3、5、7的顺序来演示:

1、如果在线程一执行到第9行代码就被CPU调度挂起:

2、线程二执行完成:

3、这个时候接着执行线程一,首先放置7这个Entry:

4、再放置5这个Entry:

5、由于5的next为null,此时扩容动作结束,导致3这个Entry丢失。

多线程情况下HashMap死循环的问题的更多相关文章

  1. Java之HashMap在多线程情况下导致死循环的问题

    PS:不得不说Java编程思想这本书是真心强大.. 学习内容: 1.HashMap<K,V>在多线程的情况下出现的死循环现象   当初学Java的时候只是知道HashMap<K,V& ...

  2. 并发场景下HashMap死循环导致CPU100%的问题

    参考链接:并发场景下HashMap死循环导致CPU100%的问题

  3. Dictionary在多线程情况下

    Add时出错 错误信息: Index was outside the bounds of the array. 详细信息: at System.Collections.Generic.Dictiona ...

  4. Java面试题之在多线程情况下,单例模式中懒汉和饿汉会有什么问题呢?

    懒汉模式和饿汉模式: public class Demo { //private static Single single = new Single();//饿汉模式 private static S ...

  5. 关于多线程情况下Net-SNMP v3 版本导致进程假死情况的跟踪与分析

    1.问题描述 在使用net-snmp对交换机进行扫描的时候经常会出现进程假死的情况(就是进程并没有死掉,但是看不到它与外界进行任何的数据交互).这时候不知道进程内部发生了什么,虽然有日志信息,但进程已 ...

  6. Singleton多种实现方式的在多线程情况下的优缺点

    一.饿汉式 缺点:不能懒加载 // 不能懒加载 public class SingletonObject1 { private static final SingletonObject1 instan ...

  7. C#多线程环境下调用 HttpWebRequest 并发连接限制

    C#多线程环境下调用 HttpWebRequest 并发连接限制 .net 的 HttpWebRequest 或者 WebClient 在多线程情况下存在并发连接限制,这个限制在桌面操作系统如 win ...

  8. 在多线程环境下使用HttpWebRequest或者调用Web Service(连接报超时问题)

    .net 的 HttpWebRequest 或者 WebClient 在多线程情况下存在并发连接限制,这个限制在桌面操作系统如 windows xp , windows  7 下默认是2,在服务器操作 ...

  9. python多线程场景下print丢失

    python多线程情况下,print输出会出现丢失的情况,而logging模块的日志输出不会. 以下是示例代码,多运行几次就会发现这个有意思的现象 # coding:utf-8 import thre ...

随机推荐

  1. Elastic Search 5.4.3 java api 入门

    首先介绍一点,es的版本从之前的2.x跳跃到5.x,很多插件要保持一致,不然会产生很多版本不兼容的问题. 首先看一个demo先熟悉一下, 具体代码在git服务器上: https://github.co ...

  2. 《Lua程序设计》第7章 迭代器与泛型for 学习笔记

    本章将介绍如何编写适用于泛型for的迭代其(Iterator).7.1 迭代器与closurehttp://www.cnblogs.com/moonlightpoet/p/5685275.html 7 ...

  3. 【LeetCode OJ】Merge Two Sorted Lists

    题目:Merge two sorted linked lists and return it as a new list. The new list should be made by splicin ...

  4. opencv3——ANN算法的使用

    最近刚转用opencv3,使用ANN算法时遇到了一些问题,记录下来. 训练神经网络的代码如下: //创建ANN Ptr<ANN_MLP> bp = ANN_MLP::create(); 设 ...

  5. 解决一起web 页面被劫持的案例

    现象 江西客户手机端连接wifi打开URL,页面上显示淘宝店铺广告,使用手机移动网络打开正常,其他地区正常. 二. 处理过程 初步分析:3g.club项目使用了CDN,目前只有江西异常,其他地区无异常 ...

  6. 《转》Python学习(18)-python函数(二)

    转自 http://www.cnblogs.com/BeginMan/p/3173328.html 一.装饰器(decorators) 装饰器的语法以@开头,接着是装饰器函数的名字.可选参数. 紧跟装 ...

  7. 【WinForm程序】注册热键快捷键切换

    重写DefWndProc事件 #region Window 消息捕获 const int WM_COPYDATA = 0x004A; public struct COPYDATASTRUCT { pu ...

  8. Ubuntu 最好用的CHM阅读器KchmViewer

    直接在“ubuntu软件中心”进行搜索安装 为什么说它是最好用?很简单!可同时显示目录和内容,中文没乱码!能实现这两点的竞争对手已经不多了,至少我是没发现.什么chmsee,gnochm,都有乱码.虽 ...

  9. [原]git的使用(五)---删除文件

    9.删除文件 [实践出真知] 创建test.txt 文件  并add 和commit到仓库 $ git status #新增加的文件test.txt On branch master Untracke ...

  10. 使用 intellijIDEA + gradle构建的项目如何debug

    在intellij IDEA里建立gradle项目(使用jett插件的web项目) 使用intellijIDEA提供的debug无效(无法进入断点) 摸索了一下,通过远程调试的方法来进行调试是可行的 ...