不止 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.computeIfAbsent("AaAa",(String key) -> {map.put("BBBB", "value");return "value";});
你会惊奇的发现这个程序一直处于Running状态,我们通过top -Hp [pid]命令查看到其中一个线程的CPU使用率接近100%,参考下图:
可以看到pid为31417的东东,我们再通过jstack -l [pid]命令查看到对应的线程为:
注意将nid=0x7ab9的16进制转为10进制就是31417。可以看到问题是发生在了computeIfAbsent方法中,我们将示例中的程序换成下面这段程序也会同样出现CPU 100%的Bug:
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方法中,自己去捞吧,嘿嘿。
原因
map.computeIfAbsent(key1, mappingFunction)
如果当前key1-hash对应的tab位(可以理解为槽)刚好是空的,在计算mappingFunction之前会
step1: 先往对应位置放一个ReservationNode占位
step2: 然后计算mappingFunction的值value,
step3: 再将value组装成最终NODE, 把占位的ReservationNode换成最终NODE;
这时如果:
mappingFunction 中用到了 当前map的computeIfAbsent方法, 很不巧 key2-hash的槽为和key1的是同一个,
因为key1已经在槽中放入了占位节点, 在处理key2时候for循环的所以处理条件都不符合 程序进入了死循环
但是如果:
key2-hash的槽位和key1的不一样, 是不会发生死循环
多线程问题:
因为ConcurrentHashMap在处理上述step1-step3是同步的, 而且在处理时候会同步获取的值, 所以是不存在线程不安全的, 纯粹是当前线程死循环
Thread1 通过cas 在槽x放了个ReservationNode(RN1), 然后假设mappingFunction执行的很慢
Thread2 在槽x和Thread竞争, cas失败没有抢到占位符; 进行下一轮for循环, 这是因为槽x中已经被放置了RN1, 所以Thread2获取到这个RN1,在执行synchronized(RN1) 时候被thread1block住
code sample:
public static void main(String[] args) throws IOException {ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();//caseA: dead loop
// map.computeIfAbsent("AA", key -> map.computeIfAbsent("BB", k->"bb"));//caseB: block, but no dead loopnew Thread(()->map.computeIfAbsent("AA", key -> waitAndGet())).start();new Thread(()->{try {TimeUnit.SECONDS.sleep(3); //delay 1 second} catch (InterruptedException e) {}map.computeIfAbsent("BB", key-> "bb");}).start();}private static String waitAndGet(){try {TimeUnit.SECONDS.sleep(20);} catch (InterruptedException e) {}return "AAA";
}
解决
怎么规避这个问题呢?只要不在递归中使用computeIfAbsent方法就好啦,或者降级用可爱的分段锁,或者升级JDK9~
不止 JDK7 的 HashMap ,JDK8 的 ConcurrentHashMap 也会造成 CPU 100%?原因与解决~相关推荐
- 不止JDK7的HashMap,JDK8的ConcurrentHashMap也会造成CPU 100%
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:朱小厮 公众号:朱小厮的博客(ID:hiddenkafka) ...
- jdk8的ConcurrentHashMap实现
ConcurrentHashMap在jdk7的使用的是分段锁(ReentrantLock),而jdk8则改为使用synchronized.同时jdk8的ConcurrentHashMap和HashMa ...
- 集合之比较接口器+Map家族的HashMap+LinkedHashMap+Hashtable+ConcurrentHashMap
集合之比较接口器+Map家族的HashMap+LinkedHashMap+Hashtable+ConcurrentHashMap 一.比较器接口 1.内置比较器 – Comparable import ...
- hashmap的特性?HashMap底层源码,数据结构?Hashmap和hashtable ConcurrentHashMap区别?
1.hashmap的特性? 允许空键和空值(但空键只有一个,且放在第一位) 元素是无序的,而且顺序会不定时改变 key 用 Set 存放,所以想做到 key 不允许重复,key 对应的类需要重写 ha ...
- JDK7中HashMap源码分析
文章目录 JDK7中的HashMap 一.JDK7中HashMap源码中重要的参数 二.JDK7中HashMap的构造方法 三.JDK7中创建一个HashMap的步骤 四.JDK7中HashMap的p ...
- Java中的Map集合及其子类HashMap,LinkedHashMap,TreeMap,ConcurrentHashMap
一 .Map public interface Map<K,V> 将键映射到值的对象.一个映射不能包含重复的键:每个键最多只能映射到一个值.此接口哦取代了Dictionary类,后者完全是 ...
- HashMap 为什么会导致 CPU 100%?文章看不懂?
来自:Java中文社群 无论是在实际工作中还是在面试中,HashMap 无疑是使用频率最高的知识点之一,所以我们需要搞懂每一个关于 HashMap 的知识点才行. 哈喽,大家好,我是老王,欢迎来到 J ...
- HashMap 为什么会导致 CPU 100%?文章看不懂?来看这个视频吧!——面试突击 006 期...
无论是在实际工作中还是在面试中,HashMap 无疑是使用频率最高的知识点之一,所以我们需要搞懂每一个关于 HashMap 的知识点才行. 哈喽,大家好,我是老王,欢迎来到 Java 面试突击,我们今 ...
- JDK8之ConcurrentHashMap源码解读
本文默认读者阅读过JDK8的HashMap源码,不再对源码中的红黑树操作进行分析. 本文主要对put().transfer().addCount()进行分析(replaceNode()源码近似于put ...
最新文章
- 哈希表的分类,创建,查找 以及相关问题解决
- 数据仓库入门(实验7)部署分析服务数据库
- 如何使用Java代码给图片增加倒影效果
- libsvm使用心得
- 贺TDSQL喜提286万QPS!本文回顾了它的十年锻造之路
- 搭建自己的博客(二十七):增加登录注册以及个人资料按钮
- 【输入法】Rime-中州韵 基本设置 附:官方定制指南
- 极大似然估计方法(Maximum Likelihood Estimate Method)
- 前端新手小白必看--最全静态网页模板网站
- Django开发教程 第一节 HelloWorld
- Windows PE (老毛桃) 介绍功能介绍
- RabbitMQ的使用(Java语言传统操作)
- c语言中除法与余数,带符号整数的除法与余数
- 一文带你复习计网中的重点知识(一万五千字长文)
- UIWebView中添加活动指示器,来化解用户等待心理
- 视觉学习笔记5——FAR Planner全局路径规划算法学习研究
- 武田宣布多项细胞疗法合作,以推进公司的新型免疫肿瘤学阵容
- python的控制结构之For、While、If循环问题
- 明星直播的品牌效应,这几个关键数据你一定要知道!
- 德国43000人汇聚线上黑客马拉松,齐愿合力干趴病毒