和ConcurrentModificationException不期而遇

好久不写文章了。

今天QA报了个bug,说一个功能在web前端有异常现象,直觉告诉我,可能是前端的问题,于是去分析log来排除自己的错误,可就在我看到一个ConcurrentModificationException后,我马上意识到问题所在了。

这是一个批量导入的功能,提交表单后,开启独立线程并马上写回响应,伺候前端跳转到进度页面,独立线程开始按行解析文本文件,一条条导入数据,每隔1s,前端向服务器发起一次查询来获取当前的导入进度,使用了spring MVC和dwr。由于上传和进度查询是独立的线程在处理,所以我当时引入了一个单例的“导入进度缓存”类,导入线程启动后会添加相应表示的相关进度信息,其中就有成功列表(ArrayList),这里每成功导入一个就要往里添加一个元素,而不幸的是,我当时忽略了dwr在往前端返回响应时是会对相关的对象进行marshall处理的,也就是说,这里会隐含一个用迭代器迭代的过程(猜测,我还没去分析dwr的源码),而读过相关jdk源码的都知道,在Java集合类里,对并发操作的检测采取了fail-fast的处理,用modCount来标记列表的被修改次数,所以QA在压力测试时才会出现dwr marshall失败的异常。

dwr的这个迭代确实是出其不意,考虑到场景并不适合CopyonWriteArrayList,所以暂时的处理方式是交给dwr去marshall之前,把list addAll 到一个空的list里,本质上会调用System.arraycopy,性能上还能接受,主要是不想加锁, 更不想有太多代码上的改动,怕引起并发症啊,而且这个功能对性能要求不高,稳定第一,所以姑且这么处理吧。有时间会再去考察下有没有合适的并发容器,这里先记上一笔。

 

后记:写文章时理所当然地认为不适合CopyonWriteArrayList,不过事后一想,太合适了。囧! 

你可能感兴趣的:(和ConcurrentModificationException不期而遇)