最近项目中出现了Tomcat占用CPU100%的情况,原以为是代码中出现死循环,后台使用jstack做了dump,发现是系统中不合理使用HashMap导致出现了死循环(注意不是死锁)。

产生这个死循环的根源在于对一个未保护的共享变量 — 一个"HashMap"数据结构的操作。当在所有操作的方法上加了"synchronized"后,一切恢复了正常。

这算jvm的bug吗?应该说不是的,这个现象很早以前就报告出来了(详细见:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6423457)。Sun的工程师并不认为这是bug,而是建议在这样的场景下应采用"ConcurrentHashMap",

回复中的原话:

This is a classic symptom of an incorrectly synchronized use of
HashMap. Clearly, the submitters need to use a thread-safe
HashMap. If they upgraded to Java 5, they could just use ConcurrentHashMap. 

所以在开发过程中应当注意这点,在多线程的环境下,尽量使用ConcurrentHashMap。

可能出现问题的地方是在扩容的时候

Java代码  
  1. void resize(int newCapacity) {
  2. Entry[] oldTable = table;
  3. int oldCapacity = oldTable.length;
  4. if (oldCapacity == MAXIMUM_CAPACITY) {
  5. threshold = Integer.MAX_VALUE;
  6. return;
  7. }
  8. Entry[] newTable = new Entry[newCapacity];
  9. transfer(newTable);
  10. table = newTable;
  11. threshold = (int)(newCapacity * loadFactor);
  12. }

这个方法本身没有问题,问题出在transfer(newTable);这个方法是用来移动oldTable里的数据到newTable里。

Java代码  
  1. /**
  2. * Transfers all entries from current table to newTable.
  3. */
  4. void transfer(Entry[] newTable) {
  5. Entry[] src = table;
  6. int newCapacity = newTable.length;
  7. for (int j = 0; j < src.length; j++) {
  8. //(1)
  9. Entry<K,V> e = src[j];
  10. if (e != null) {
  11. src[j] = null;
  12. do {
  13. //(2)
  14. Entry<K,V> next = e.next;
  15. int i = indexFor(e.hash, newCapacity);
  16. //(3)
  17. e.next = newTable[i];
  18. newTable[i] = e;
  19. e = next;
  20. } while (e != null);
  21. }
  22. }
  23. }

下面分析可能出现的情况,假设原来oldTable里存放a1,a2的hash值是一样的,那么entry链表顺序是:

P1:oldTable[i]->a1->a2->null

P2:oldTable[i]->a1->a2->null

线程P1运行到(1)下面这行时,e=a1(a1.next=a2),继续运行到(2)下面时,next=a2。这个时候切换到线程P2,线程P2执行完这个链表的循环。如果恰a1,a2在新的table中的hash值又是一样的,那么此时的链表顺序是:

主存:newTable[i]->a2->a1->null

注意这个时候,a1,a2连接顺序已经反了。现在cpu重新切回P1,在(3)这行以后:e.next = newTable[i];即:a1.next=newTable[i];

newTable[i]=a1;

e=a2;

开始第二次while循环(e=a2,next=a1):

a2.next=newTable[i];//也就是a2.next=a1

newTable[i]=a2

e=a1

开始第三次while循环(e=a1,next=null)

a1.next=newTable[i];//也就是a1.next=a2

这个时候a1.next=a2,a2.next=a1,形成回环了,这样就造成了死循环,在get操作的时候next永远不为null,造成死循环。

可以看到很偶然的情况下会出现死循环,不过一旦出现后果是非常严重的,多线程的环境还是应该用ConcurrentHashMap。

java中HashMap在多线程环境下引起CPU100%的问题解决相关推荐

  1. 【死链】JDK1.7中HashMap在多线程环境的并发问题源码分析

    文章目录 一.HashMap在JDK1.7中的并发问题 二.死链如何产生? 三.如何解决HashMap并发问题 参考文献 一.HashMap在JDK1.7中的并发问题 在JDK1.7中的HashMap ...

  2. 多线程环境下HashMap导致CPU100%

    引言 昨天早上线上系统开始作业了一段时间以后,突然收到服务器报警,服务器CPU持续占用100%,导致线上系统不能正常使用,我登录服务器top了一下,发现java进程占用cpu400%, 由于前天晚上上 ...

  3. Java多线程之单例模式在多线程环境下的安全问题

    Java多线程之单例模式在多线程环境下的安全问题 目录: 单例模式基本概念 单线程下的单例模式 多线程下的单例模式 单例模式volatile分析 1. 单例模式基本概念 基本概念转载自:单例模式|菜鸟 ...

  4. java基础--java中HashMap原理

    java中HashMap原理 内推军P21 P22 1.为什么用HashMap? HashMap是一个散列桶(数组和链表),它存储的内容是键值对(key-value)映射HashMap采用了数组和链表 ...

  5. HttpClient在多线程环境下踩坑总结

    HttpClient在多线程环境下踩坑总结 问题现场 在多线程环境下使用HttpClient组件对某个HTTP服务发起请求,运行一段时间之后发现客户端主机CPU利用率呈现出下降趋势,而不是一个稳定的状 ...

  6. java中HashMap、ArrayList、HashSet的存储原理及遍历方式汇总

    HashMap类 底层存储方式:1. HashMap在JDK1.7之前底层是用数组+链表的方式存储的:在JDK1.8之后底层是用数组+链表+红黑树存储的;2.HashMap里面实现了一个静态内部类En ...

  7. 多线程环境下,程序真是危机四伏

    姿势在不断的更新迭代, 太卷了. 你管这也叫线程安全? 最近大意了,竟然想将<面试官:实现一个带值变更通知能力的Dictionary>一文中的临界锁只应用到写操作. 内心旁白:读操作又不会 ...

  8. 单例模式的5种实现方式,以及在多线程环境下5种创建单例模式的效率

    这段时间从头温习设计模式.记载下来,以便自己复习,也分享给大家. [java] view plaincopy package com.iter.devbox.singleton; /** * 饿汉式 ...

  9. flask keras 多线程环境下加载模型

    keras 多线程环境下加载模型 Tensor Tensor is not an element of this graph. 问题场景 keras 使用flask 发布深度学习模型服务,模型有一个定 ...

最新文章

  1. 【CF EDU59 E】 Vasya and Binary String (DP)
  2. PHP下ajax跨域的解决方案之CORS
  3. 太相信书的人,格局不会太大
  4. 微型计算机工业控制技术,基于ARM的微机原理与接口技术(STM32嵌入式系统架构编程与应用嵌入式与工业控制技术高等学校电子信息类专业系列教材)...
  5. gamma分布_深度学习需要掌握的 13 个概率分布(附代码)
  6. html 页间传送数据,js 不同页面间传递值并取值,html不同页面间数据传递
  7. php 登陆信息 传递,PHP传递POST信息
  8. 【LeetCode】整数反转
  9. [洛谷P3228] [HNOI2013]数列
  10. Blender插件之Panel
  11. 页面修饰框架SiteMesh的简单使用
  12. STARK Arithmetization
  13. Python--pyaudio声卡录音
  14. 谷歌身份验证器二维码
  15. Gartner 公布 2022 新兴技术成熟度曲线,这些技术趋势最值得关注
  16. Unreal4引擎开发学习日志
  17. redis rce漏洞安全_从重新定位到RCE:我从广告到网络安全的旅程
  18. 理解JavaScriptES6中的TDZ(暂时性死区)
  19. iTechTag:声望创造价值
  20. 网络基础-第三章:认识网线制作工具

热门文章

  1. Java线程池详解学习:ThreadPoolExecutor
  2. vue.js与ajax删除,javascript - VueJs和VueResource,从Ajax请求中删除标头字段 - 堆栈内存溢出...
  3. python小课堂25_python小课堂-循环
  4. python删除重复值所在的行数_使用python读取txt文件的内容,并删除重复的行数方法...
  5. 【Netty】入门Netty官方例子解析(三)处理一个基于流的传输 TCP粘包和拆包问题分析和解决
  6. 万字总结:学习MySQL优化原理,这一篇就够了!
  7. chromium android分析,Chromium Android工程迁移编译过程
  8. 常用的数据交换格式有哪些_Linux后台开发6大常用的开源库,让你在同行中脱颖而出...
  9. selenium + python自动化测试环境搭建
  10. 定时分量和直流分量_直流电机效率测试的计算与纹波因数及波形因数的计算