线程并发安全导致内存溢出

整个网站访问不了,后台日志内存溢出,提出了个致命单,找到问题后,整理成了案例,供培训使用。

原因:

流量统计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,也就不用再清空了。

原文地址:https://www.cnblogs.com/onlywujun/p/2938211.html