生产环境遇到的hashMap非线程安全问题java.lang.thread.waiting

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了生产环境遇到的hashMap非线程安全问题java.lang.thread.waiting相关的知识,希望对你有一定的参考价值。

写在前面:工作有几年了,从入门到现在,遇到也解决了一些问题。(当然,框架级别的暂时还没有)一直以来,都是从博客园以及其他各大社区搜罗出来的各种fix方法。目前稍有闲暇时间,在看过大V沈剑的博文后,我也鼓起勇气来书写博客,记录工作中遇到和解决的问题(其中当然也包括我在博园获取的各种解决方法;能找到原博文的小弟一定会注明出处。)因为总觉得自己水平不够,怕写出来的文章误导了别人。以下是这周生产环境遇到的一个问题,写出来供大家参考。

现象

  周五一大早,车子都没停稳(电动车),群里就开始在询问谁最近的代码有比较耗时耗性能的操作,大家都说没有。然后就是一张截图:

技术分享

cpu已经飙到1600+,这个问题还是比较严重的。随后联系到运维组给出tomcat运行日志排查是否有错误。经过排查日志发下如下错误:

技术分享

看到后飞速坐到位置上,去查询对应的错误代码;

 

分析和搜索:

  以下是部分代码截图:

 1     public static Map<String,String> cityCodeMap = new HashMap<String, String>();
 2     
 3     public static Map<String,String> provinceMap = new HashMap<String, String>();
 4     
 5     public static List<String> sensitiveWordList = new ArrayList<String>();
 6     
 7     public static String getCityCodeByNumber(String phoneNumber)
 8     {
 9         String start = StringUtils.substring(phoneNumber, 0, 7);
10         if(StringUtils.isEmpty(start))
11         {
12             return "000";
13         }
14         Map<String, String> cityCodes = getCityCodes();
15         String cityCode = cityCodes.get(start);
16         if(StringUtils.isEmpty(cityCode))
17         {
18             return "000";
19         }
20         return cityCode;
21     }

 

主要用于获取手机号码的号段对应的城市编码,项目的某个模块(不能打广告吧)需要用到。并且每次调用都用用到;咋一看,感觉没啥问题,项目中好多地方都是这么干的。接着又在测试环境和本地环境跑一边代码,都运行正常。

然后就搜索了java.lang.thread.waiting 这个异常,在这篇博文中搜到相关问题,http://www.cnblogs.com/zhengyun_ustc/archive/2013/03/18/tda.html

才发现可能是多线程引发的问题。

这里列举两个比较解释特别详细的博文,源码跟踪和分析过程都非常详细。

http://coding-geek.com/how-does-a-hashmap-work-in-java/

https://coolshell.cn/articles/9606.html

  

  此种情况虽然是概率发生的,但是在并发量比较大的情况下,还是及其危险的。如果发现不及时,很有可能导致单点故障甚至整个集群不可用。又根据自己项目代码的情况分析,主要是因为在初始化中,循环向map中put新元素导致map扩容rehash时产生了死循环。

初始化代码:

    public static Map<String,String> getCityCodes() {
        if(cityCodeMap.isEmpty())
        {
            Set<String> keySet = ResourceBundle.getBundle("config/citynum").keySet();
            for (String key : keySet) {
                String cityCode = PropertiesUtil.getKey("config/citynum",key);
                cityCodeMap.put(key, cityCode);
            }
        }
        return cityCodeMap;
    }

ps:此处的citynum中有几万个键值对。

解决办法:

  1. Hashtable
  2. ConcurrentHashMap
  3. Synchronized Map

可自行搜索实现原理,很多大神、大仙儿都阐述的比我详细。

都说程序员都是懒人,我不认同。我们只不过是想用最少的代码去解决问题。所以,我们的改良方案就是把HashMap直接换成ConcurrentHashMap

技术分享

 

以上是关于生产环境遇到的hashMap非线程安全问题java.lang.thread.waiting的主要内容,如果未能解决你的问题,请参考以下文章

Java线程安全和非线程安全

java8 中concurrenthashmap数据结构和HashMap一样,且线程安全 为啥还要HashMap

所谓线程安全和非线程安全

多线程环境下,HashMap 为什么会出现死循环?

java多线程3.设计线程安全类

线程安全和非线程安全