一次关于fastjson的多线程并发问题的排查
目录
-
问题描述
外部调用repair补全接口,传参为上下文数据data,补全工具的相关配置(包括反射的类名、方法名、参数类型等等),将工具返回的字段补充到上下文中。
现在存在一个问题:单次调用接口,能够正常返回补全字段。但是并发调用接口,有的就不能返回补全字段。于是,就怀疑到是多线程并发问题。
-
代码流程
-
部分代码示例
- 多线程调用代码
- 线程内部代码
-
问题排查流程
- 本地使用多线程模拟并发,调用repair方法。
- 线程内将补全工具的结果打印出来,发现存在code为成功,但字段为空的情况;
- 于是再进一步的将调用补全工具的参数打印出来,发现入参有为空的时候。
- 本地使用多线程模拟并发,调用repair方法。
- 这个多线程共享的是上下文data和补全工具相应的配置toolconfig,于是,怀疑这两个值被修改了。经上图排查发现,获取参数前后的上下文data的引用地址不一样。
- 开始验证,是否是因为data被修改导致获取不到参数?如下图,运行后发现问题解决了。果然是data被修改了。
- 然后,开始找data是在哪被修改的?如下图,发现多线程调用之后的结果,会使用data.putall将补全的字段放入到上下文。于是,开始排查是不是这引起的。将这个代码注释掉,发现数据也正常了,问题得到解决。从而得知,JSONObject的putAll存在多线程并发问题,不是线程安全的。
- 最后,先查看JSONObject底层是使用的hashmap,先看有没有线程安全的。经发现,JSONObject有构造函数,可以传入Map。于是,将上下文的data的map改为线程安全的map进行验证。如下图,得到了正确的结果。
-
总结
- fastjson底层是map,不是线程安全的。在多线程中使用的时候,注意它的使用。
- 对于多线程并发问题,注意找线程之间共享的变量。
- 使用线程池,注意线程池的四种拒绝策略。如本案例,使用的就是丢给调用线程处理