線程並發安全導致內存溢出


整個網站訪問不了,后台日志內存溢出,提出了個致命單,找到問題后,整理成了案例,供培訓使用。

原因:

流量統計FlowUtil類使用兩個static的List來裝載流量信息實體bean。
      用戶每次點擊都會將一個產生一個bean並加入到第一個List1中,當List1里的bean到一定數量時(可在后台配置緩存大小),List1將所有的實體bean復制到 List2中,然后List1清空繼續接收新的bean,這時List2開啟一個新線程異步去將bean插入數據庫,然后清空。

Method add(bean){

list1.add(bean);

        if (list1.size() >= cacheSize)

        {

            List2.addAll(list1);

            list1.clear();

            new Thread({

                try

{

insertDB(list2);

list2.clear();

}

                 catch (ApplicationException e)

                  {

                  }

}).start();

        }

}
      整個操作沒有做同步鎖定,如果並發量大,List2還沒完成插入數據庫的操作,List1又將新接收的bean全部加入到List2中,又發起一個新線程去插數據庫,如果這個線程跑在之前那個線程前,因為這時List2之前的bean是沒有被清空的,再插入數據庫的時候,id就會重復,就會拋出違反唯一性約束異常,就不會執行下一行清空List2的代碼,直接結束了。以后無論如何,每次操作都會拋出違反唯一性約束異常了,這個時候List2就會不斷地接收List1傳進來的bean,直到內存溢出。

 

分析:如果在try-catach后的finally里去clear清空list2,不會出現這種現象,但也有線程安全問題。這段代碼存在兩個問題,一個是線程安全,一個就是try-catach段代碼,沒考慮到異常的情況,在try里每段代碼都應考慮如果執行到這段會不會有影響,如果有就在finnally里執行。

 

 

解決方案:同步數據轉交那段代碼,並不再使用static的List2,List1每次轉交都給一個全新的List去執行插數據庫。

Method add(bean){

list1.add(bean);

        if (list1.size() >= cacheSize)

        {   

            List list2 = new List();

            Synchronized{

list2.addAll(list1);

                  list1.clear();

            }

            new Thread({

                try

{

insertDB(list2);

}

                 catch (ApplicationException e)

                  {

                  }

}).start();

        }

}
由於每次都使用一個新的list2,也就不用再清空了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM