和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,不过事后一想,太合适了。囧! 

posted on   晋哥哥  阅读(457)  评论(1编辑  收藏  举报

编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述

导航

< 2011年10月 >
25 26 27 28 29 30 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示