一次关于fastjson的多线程并发问题的排查

目录

问题描述

代码流程

部分代码示例

问题排查流程

总结


  • 问题描述

外部调用repair补全接口,传参为上下文数据data,补全工具的相关配置(包括反射的类名、方法名、参数类型等等),将工具返回的字段补充到上下文中。

现在存在一个问题:单次调用接口,能够正常返回补全字段。但是并发调用接口,有的就不能返回补全字段。于是,就怀疑到是多线程并发问题。

  • 代码流程

     一次关于fastjson的多线程并发问题的排查

  • 部分代码示例

    • 多线程调用代码

一次关于fastjson的多线程并发问题的排查

  • 线程内部代码

一次关于fastjson的多线程并发问题的排查

  • 问题排查流程

    • 本地使用多线程模拟并发,调用repair方法。
      • 线程内将补全工具的结果打印出来,发现存在code为成功,但字段为空的情况;
      • 于是再进一步的将调用补全工具的参数打印出来,发现入参有为空的时候。

一次关于fastjson的多线程并发问题的排查

  • 这个多线程共享的是上下文data和补全工具相应的配置toolconfig,于是,怀疑这两个值被修改了。经上图排查发现,获取参数前后的上下文data的引用地址不一样
  • 开始验证,是否是因为data被修改导致获取不到参数?如下图,运行后发现问题解决了。果然是data被修改了

一次关于fastjson的多线程并发问题的排查

  • 然后,开始找data是在哪被修改的?如下图,发现多线程调用之后的结果,会使用data.putall将补全的字段放入到上下文。于是,开始排查是不是这引起的。将这个代码注释掉,发现数据也正常了,问题得到解决。从而得知,JSONObject的putAll存在多线程并发问题,不是线程安全的。

一次关于fastjson的多线程并发问题的排查

  • 最后,先查看JSONObject底层是使用的hashmap,先看有没有线程安全的。经发现,JSONObject有构造函数,可以传入Map。于是,将上下文的data的map改为线程安全的map进行验证。如下图,得到了正确的结果。

一次关于fastjson的多线程并发问题的排查

  • 总结

    • fastjson底层是map,不是线程安全的。在多线程中使用的时候,注意它的使用。
    • 对于多线程并发问题,注意找线程之间共享的变量。
    • 使用线程池,注意线程池的四种拒绝策略。如本案例,使用的就是丢给调用线程处理

一次关于fastjson的多线程并发问题的排查