不止JDK7的HashMap,JDK8的ConcurrentHashMap也会造成CPU 100%

2024-02-10 22:38

本文主要是介绍不止JDK7的HashMap,JDK8的ConcurrentHashMap也会造成CPU 100%,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

大家可能都听过JDK7中的HashMap在多线程环境下可能造成CPU 100%的现象,这个由于在扩容的时候put时产生了死链,由此会在get时造成了CPU 100%。这个问题在JDK8中的HashMap获得了解决。其实JDK7中的HashMap在多线程环境下不止只有CPU 100%这一共怪异现象,它还可能造成插入的数据丢失,有兴趣的读者可以自行了解下。

对于HashMap多线程的问题,我们通常会这么反问:HashMap设计上就不是多线程安全的,何必要去在多线程环境下用呢?的确如此,我们不会傻到显式的在多线程环境下调用,但是又可能在你所关注的视角范围外是多线程的,其隐式地让HashMap置于多线程环境下了,这个又难以一下子察觉到。再者,对于HashMap多线程的问题,我们很多时候推荐使用ConcurrentHashMap来代替HashMap应用于多线程的环境,很不巧的是ConcurrentHashMap也有可能会造成CPU 100%的异常现象。这个怪异现象存在于JDK8的ConcurrentHashMap中,在JDK9中已经得到修复,可以参见:https://bugs.openjdk.java.net/browse/JDK-8062841

什么情况下JDK8的ConcurrentHashMap会出现这个Bug呢?首先我们来运行一下这段代码:

Map<String, String> map = new ConcurrentHashMap<>();
map.computeIfAbsent("AaAa",key -> map.computeIfAbsent("BBBB", key2 -> "value"));

你会惊奇的发现这个程序一直处于Running状态,我们通过top -Hp [pid]命令查看到其中一个线程的CPU使用率接近100%,参考下图:


可以看到pid为31417的东东,我们再通过jstack -l [pid]命令查看到对应的线程为:

注意将nid=0x7ab9的16进制转为10进制就是31417。可以看到问题是发生在了computeIfAbsent方法中,我们将示例中的程序换成下面这段程序也会同样出现CPU 100%的Bug:

map.computeIfAbsent("AaAa",(String key) -> {map.put("BBBB", "value");return "value";});

问题的关键在于递归使用了computeIfAbsent方法,笔者在stackoverflow上还搜索到了同类型的问题,下面的示例程序中调用fibonacci方法同样也会造成CPU 100%.

static Map<Integer, Integer> concurrentMap = new ConcurrentHashMap<>();public static void main(String[] args) {System.out.println("Fibonacci result for 20 is" + fibonacci(20));
}static int fibonacci(int i) {if (i == 0)return i;if (i == 1)return 1;return concurrentMap.computeIfAbsent(i, (key) -> {System.out.println("Value is " + key);return fibonacci(i - 2) + fibonacci(i - 1);});
}

至于为什么会发生这个BUG,答案就在ConcurrentHashMap中的computeIfAbsent方法中,自己去捞吧。

怎么规避这个问题呢?

只要不在递归中使用computeIfAbsent方法就好啦,或者降级用可爱的分段锁,或者升级JDK9~

【End】

老王给大家准备一份「Java最常见200+面试题全解析」,助力大家找到更好的工作,这份面试题包含的模块:

  • Java、JVM 最常见面试题解析

  • Spring、Spring MVC、MyBatis、Hibernate 面试题解析

  • MySQL、Redis 面试题解析

  • RabbitMQ、Kafka、Zookeeper 面试解析

  • 微服务 Spring Boot、Spring Cloud 面试解析

扫描下面二维码付费阅读

关注下方二维码,订阅更多精彩内容。

转发朋友圈,是对我最大的支持。

这篇关于不止JDK7的HashMap,JDK8的ConcurrentHashMap也会造成CPU 100%的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/698192

相关文章

Java中HashMap的用法详细介绍

《Java中HashMap的用法详细介绍》JavaHashMap是一种高效的数据结构,用于存储键值对,它是基于哈希表实现的,提供快速的插入、删除和查找操作,:本文主要介绍Java中HashMap... 目录一.HashMap1.基本概念2.底层数据结构:3.HashCode和equals方法为什么重写Has

linux系统上安装JDK8全过程

《linux系统上安装JDK8全过程》文章介绍安装JDK的必要性及Linux下JDK8的安装步骤,包括卸载旧版本、下载解压、配置环境变量等,强调开发需JDK,运行可选JRE,现JDK已集成JRE... 目录为什么要安装jdk?1.查看linux系统是否有自带的jdk:2.下载jdk压缩包2.解压3.配置环境

SysMain服务可以关吗? 解决SysMain服务导致的高CPU使用率问题

《SysMain服务可以关吗?解决SysMain服务导致的高CPU使用率问题》SysMain服务是超级预读取,该服务会记录您打开应用程序的模式,并预先将它们加载到内存中以节省时间,但它可能占用大量... 在使用电脑的过程中,CPU使用率居高不下是许多用户都遇到过的问题,其中名为SysMain的服务往往是罪魁

MySQL中优化CPU使用的详细指南

《MySQL中优化CPU使用的详细指南》优化MySQL的CPU使用可以显著提高数据库的性能和响应时间,本文为大家整理了一些优化CPU使用的方法,大家可以根据需要进行选择... 目录一、优化查询和索引1.1 优化查询语句1.2 创建和优化索引1.3 避免全表扫描二、调整mysql配置参数2.1 调整线程数2.

Linux进程CPU绑定优化与实践过程

《Linux进程CPU绑定优化与实践过程》Linux支持进程绑定至特定CPU核心,通过sched_setaffinity系统调用和taskset工具实现,优化缓存效率与上下文切换,提升多核计算性能,适... 目录1. 多核处理器及并行计算概念1.1 多核处理器架构概述1.2 并行计算的含义及重要性1.3 并

Linux下进程的CPU配置与线程绑定过程

《Linux下进程的CPU配置与线程绑定过程》本文介绍Linux系统中基于进程和线程的CPU配置方法,通过taskset命令和pthread库调整亲和力,将进程/线程绑定到特定CPU核心以优化资源分配... 目录1 基于进程的CPU配置1.1 对CPU亲和力的配置1.2 绑定进程到指定CPU核上运行2 基于

Java进程CPU使用率过高排查步骤详细讲解

《Java进程CPU使用率过高排查步骤详细讲解》:本文主要介绍Java进程CPU使用率过高排查的相关资料,针对Java进程CPU使用率高的问题,我们可以遵循以下步骤进行排查和优化,文中通过代码介绍... 目录前言一、初步定位问题1.1 确认进程状态1.2 确定Java进程ID1.3 快速生成线程堆栈二、分析

conda安装GPU版pytorch默认却是cpu版本

《conda安装GPU版pytorch默认却是cpu版本》本文主要介绍了遇到Conda安装PyTorchGPU版本却默认安装CPU的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的... 目录一、问题描述二、网上解决方案罗列【此节为反面方案罗列!!!】三、发现的根本原因[独家]3.1 p

Linux CPU飙升排查五步法解读

《LinuxCPU飙升排查五步法解读》:本文主要介绍LinuxCPU飙升排查五步法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录排查思路-五步法1. top命令定位应用进程pid2.php top-Hp[pid]定位应用进程对应的线程tid3. printf"%

Java遍历HashMap的6种常见方式

《Java遍历HashMap的6种常见方式》这篇文章主要给大家介绍了关于Java遍历HashMap的6种常见方式,方法包括使用keySet()、entrySet()、forEach()、迭代器以及分别... 目录1,使用 keySet() 遍历键,再通过键获取值2,使用 entrySet() 遍历键值对3,