1. 程式人生 > >生產環境遇到的hashMap非線程安全問題java.lang.thread.waiting

生產環境遇到的hashMap非線程安全問題java.lang.thread.waiting

線程安全 單點故障 pty isempty pan mage 分享 tab 記錄

寫在前面:工作有幾年了,從入門到現在,遇到也解決了一些問題。(當然,框架級別的暫時還沒有)一直以來,都是從博客園以及其他各大社區搜羅出來的各種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