记因缓存返回引用对象导致的线程安全问题
记因缓存返回引用对象导致的线程安全问题
背景
前几天处理生产环境问题的时候,遇到一个因为缓存写的不太好,直接返回了引用对象,导致的线程安全问题。
日志信息
日志如下,部分与公司有关的信息已经删掉。
1 | 2020-06-01 14:37:49 [ERROR] [task-3] xxx -失败! |
排查过程
两个线程同时抛出一个异常点,在被我删掉的日志中显示这两个线程正在执行同一行代码。
两个线程执行的同一行代码其实是一个foreach
遍历,且经我检查,并没有对集合中的对象进行remove
的操作。
继续排查发现,这个foreach
操作的对象是从一个缓存中获取到的,于是顺着这个缓存开始继续排查。跟着代码调用逻辑发现,有直接往这个缓存返回的list中做add的操作。
检查后发现这个我们系统自己使用ConcurrentHashMap封装的缓存直接返回了引用对象ArrayList
,且可读可写。
结论
最后其实结论就是缓存使用不当,导致多线程操作ArrayList对象,一边遍历一边插入新的元素,导致迭代器在做check的时候抛出了一场,出现了线程安全的问题。
解决方案
- 返回结果的时候拷贝,不要直接返回引用对象
- 使用线程安全的List实现
- 还有啥?记一个TODO