记一次线上故障--HashMap在多线程条件下运行造成CPU 100%

       Java程序关联的CPU打满100%一般都是程序编写不规范,引发的死循环造成。为什么HashMap的组装数据及调用会造成死循环呢,这里需要从HashMap的底层数据结构分析原因。

一.JDK1.8 HashMap的数据结构

数组 + 链表 + 红黑树

二.结合问题代码与HahsMap底层结构分析

2.1 线上的问题代码

public Map<String,Object> selectByName(List<String> nameList) {
        Map<String,Object> dataMap = new HashMap();
        if (CollUtil.isNotEmpty(dataList)){
            dataList.parallelStream().forEach(item->{
		         //业务处理
                if (StringUtils.isNotEmpty(TypeVO.getType())){
                    dataMap.put(item.getName(),TypeVO);
                }
            });
        }
        return dataMap;
}

2.2 HashMap的扩容底层实现如下

void transfer(Entry[] newTable) {
        Entry[] src = table;
        int newCapacity = newTable.length;
        for (int j = 0; j < src.length; j++) {
            Entry<K,V> e = src[j];
            if (e != null) {
                src[j] = null;
                do {
                    Entry<K,V> next = e.next;
                    int i = indexFor(e.hash, newCapacity);
                    e.next = newTable[i];
                    newTable[i] = e;
                    e = next;
                } while (e != null);    
            }
        }
 }

问题就出现在了while (e != null),从上面的代码看来,每一个线程进来都先执行dataMap = new HashMap();这个时候dataMap是空的,所以在执行下面的操作的时候进入了某一个不可以随意更改状态的代码中,再加上高并发,一直被new HashMap(),while一直被执行,变成了死循环。cpu就瞬间飙升到100%,一直持续到请求数降低的时候。

三.解决办法

重构问题代码,多线程并发扩容时就会出现环形引用的问题,从而导致死循环的出现,一直死循环就会导致 CPU 运行 100%,所以在多线程使用时,我们需要使用 ConcurrentHashMap 来替代 HashMap,ConcurrentHashMap是线程安全的Map,适合在多线程环境下稳定运行。

public ConcurrentHashMap<String,Object> selectByName(List<String> nameList) {
        ConcurrentHashMap<String,Object> dataMap = new ConcurrentHashMap();
        if (CollUtil.isNotEmpty(dataList)){
            dataList.parallelStream().forEach(item->{
                //业务处理
                if (StringUtils.isNotEmpty(typeVO.getType())){
                    dataMap.put(item.getName(),typeVO);
                }
            });
        }
        return dataMap;
 }

四.附录:定位到CPU打满100%问题代码的命令

4.1 查看Linux主机各进程CPU使用率情况

top -c

4.2 通过4.1查出cpu使用率最高的进程pid,查询该pid进程底下的线程使用情况

top -Hp <PID>

4.3 通过4.2查出cpu使用率最高的线程tid,为了便于在jstack中查询,先把线程tid转换成16进制,如1f529

printf "%x\n" <TID>

4.4  通过jstack命令查看相应线程的堆栈

jstack <PID> | grep <16进制的TID> -A 30

4.5 整套流程命令示例

1.top -c  查出cpu使用率最高进程PID为127387
2.top -Hp 127387  查出线程使用率最高TID为128739
3.printf "%x\n" 128739 查出TID的16进制为1f529
4.jstack 127387 | grep 1f529 -A 30   查出堆栈信息,问题代码

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潇潇雨歇_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值