今天开发环境压测的时候出现cpu用满了情况,看线程堆栈,一堆线程都停留在org.apache.commons.collections4.map.AbstractHashedMap.put(AbstractHashedMap.java:285),查看google源代码

public Object put(Object key, Object value) {
        key = convertKey(key);
        int hashCode = hash(key);
        int index = hashIndex(hashCode, data.length);
        HashEntry entry = data[index];
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) {
                Object oldValue = entry.getValue();
                updateEntry(entry, value);
                return oldValue;
            }
            entry = entry.next;
        }
        
        addMapping(index, hashCode, key, value);
        return null;
    }

方法是非线程安全的,而addMapping方法会触发ensureCapacity扩容,而并发扩容就会容易导致死循环, 具体原因参考文章https://www.cnblogs.com/dongguacai/p/5599100.html

解决方法:

1、使用ConcurrentHashMap。

2、使用Collections.synchronizedMap(Mao<K,V> m)方法把HashMap变成一个线程安全的Map。

我copy了部分文章内容

正常的扩容过程

我们先来看下单线程情况下,正常的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。在复杂的生产环境中,这种情况尽管不常见,但是可能会碰到。

hashmap引起死循环的更多相关文章

  1. 图解集合5:不正确地使用HashMap引发死循环及元素丢失

    问题引出 前一篇文章讲解了HashMap的实现原理,讲到了HashMap不是线程安全的.那么HashMap在多线程环境下又会有什么问题呢? 几个月前,公司项目的一个模块在线上运行的时候出现了死循环,死 ...

  2. 多线程下HashMap的死循环问题

    多线程下[HashMap]的问题: 1.多线程put操作后,get操作导致死循环.2.多线程put非NULL元素后,get操作得到NULL值.3.多线程put操作,导致元素丢失. 本次主要关注[Has ...

  3. HashMap的原理与实 无锁队列的实现Java HashMap的死循环 red black tree

    http://www.cnblogs.com/fornever/archive/2011/12/02/2270692.html https://zh.wikipedia.org/wiki/%E7%BA ...

  4. HashMap陷入死循环的例子

    //使用这个例子可以模拟HashMap陷入死循环的效果,可能需要执行多次才会出现. 1 package com.hanzi; import java.util.HashMap; public clas ...

  5. 【转】Java HashMap的死循环

    问题的症状 从前我们的Java代码因为一些原因使用了HashMap这个东西,但是当时的程序是单线程的,一切都没有问题.后来,我们的程序性能有问题,所以需要变成多线程的,于是,变成多线程后到了线上,发现 ...

  6. 集合(五)不正确地使用HashMap引发死循环及元素丢失

    前一篇文章讲解了HashMap的实现原理,讲到了HashMap不是线程安全的.那么HashMap在多线程环境下又会有什么问题呢? 几个月前,公司项目的一个模块在线上运行的时候出现了死循环,死循环的代码 ...

  7. JDK(九)JDK1.7源码分析【集合】HashMap的死循环

    前言 在JDK1.7&1.8源码对比分析[集合]HashMap中我们遗留了一个问题:为什么HashMap在调用resize() 方法时会出现死循环?这篇文章就通过JDK1.7的源码来分析并解释 ...

  8. 面试突击17:HashMap除了死循环还有什么问题?

    面试合集:https://gitee.com/mydb/interview 本篇的这个问题是一个开放性问题,HashMap 除了死循环之外,还有其他什么问题?总体来说 HashMap 的所有" ...

  9. Java HashMap的死循环

    在淘宝内网里看到同事发了贴说了一个CPU被100%的线上故障,并且这个事发生了很多次,原因是在Java语言在并发情况下使用HashMap造 成Race Condition,从而导致死循环.这个事情我4 ...

  10. HashMap多线程死循环问题

    HashMap通常会用一个指针数组(假设为table[])来做分散所有的key,当一个key被加入时,会通过Hash算法通过key算出这个数组的下标i,然后就把这个<key, value> ...

随机推荐

  1. vsftp服务器同步文件

    首先vsftp同步文件,并没有好的解决方案,网上有一些软件是可以定时同步文件的,我是用的是linux上的一款工具Rsync 首先先了解一下Rsync与scp的区别:点我 1.安装Rsync 两种安装方 ...

  2. MyEclipse持续性开发教程:用JPA和Spring管理数据(一)

    MyEclipse开年钜惠 在线购买低至75折!立即开抢>> [MyEclipse最新版下载] 本教程介绍了MyEclipse中的一些基于JPA / Spring的功能.有关设置JPA项目 ...

  3. Python 日志管理封装

    封装python中的logging方便日常使用 class Logger(object): level_mapping = { 'debug': logging.DEBUG, 'info': logg ...

  4. select 从应用层到内核实现解析

    在一个应用中,如果需要读取多个设备文件,这其中有多种实现方式: 1.使用一个进程,并采用同步查询机制,不停的去轮询每一个设备描述符,当设备描述符不可用时,进程睡眠. 2:使用多个进程或者线程分别读取一 ...

  5. torch7 安装 并安装 hdf5模块 torch模块 nn模块 (系统平台为 ubuntu18.04 版本)

    今年的CCF A会又要开始投稿了,实验室的师弟还在玩命的加实验,虽然我属于特殊情况是该从靠边站被老板扶正但是实验室的事情我也尽力的去帮助大家,所以师弟在做实验的时候遇到了问题也会来问问我,这次遇到的一 ...

  6. Ubuntu 速配指南:开启3D桌面特效

     http://www.lupaworld.com/article-205494-1.html 安装compizconfig-settings-manager(以下简称ccsm) 在终端里输入:sud ...

  7. ElasticSearch(七):ElasticSearch集群的搭建

    由于资源有限,使用是一台机器上安装三个elasticSearch服务端组成的集群. 1. 安装elasticSearch6.3.2 将原本安装的elasticSearch6.3.2复制两份,分别重新命 ...

  8. centos安装redis +RedisDesktopManager连接redis

    1.先到Redis官网(redis.io)下载redis安装包 wget http://download.redis.io/releases/redis-5.0.4.tar.gztar xzf red ...

  9. LG4768 [NOI2018]归程

    题意 题目背景 本题因为一些原因只能评测16组数据. 剩下的四组数据:https://www.luogu.org/problemnew/show/U31655 题目描述 本题的故事发生在魔力之都,在这 ...

  10. 重磅来袭,开源Asp.Net MVC网上商城BrnShop正式发布,提供源码下载(转)

    BrnShop网上商城是以Asp.Net mvc3为基础开发的网上商城,源代码完全开源(企业版的源代码目前还没有完全整理完成,一旦整理完成也全部开源). 啥话也不说了,直接上源码:下载源码(由于公司服 ...