引入
前几天的日常巡查时发现生产服务上有空指针的异常,但是业务又没有影响,然后通过查看对应的堆栈异常信息,总算是定位到了问题,但是这个问题又有点奇怪。ThreadLocal
的 get()
返回了空指针,但是是在当前的线程的运行中去取值,这个怎么会有问题呢?
伪代码如下:
1 | class ThreadUtil { |
分析
首先我们根据结果导向性原则进行判断这个问题,其次 ThreadLocal
通过将当前线程作为 key
存储来存储对应当前线程的共享信息,接着再去查看一下对应的源码逻辑,最终就可以得出在原有的线程被回收后,所对应的 value
没有再被引用,从而导致返回 null
。
分析到这里,可能一般的问题都会被解决了,可是你仔细去看看伪代码,就会发现,还存在一个特殊的注解 @Async
。而这个注解的意思是新建一个线程,然后用新线程执行调用这个方法,也就是异步调用。
我想这个问题其实已经很显而易见了,出现空指针的根本原因在于,主线程在调用异步方法后,并没有传递主线程上使用的 Object
对象给新线程,从而导致新线程在去获取对应的 Object
对象时,因为所属的线程并不相同,自而然无法从 ThreadLocal
中去拿到主线程共享的对象了。
如果对 ThreadLocal
不是很了解的话,还是首先建议先了解下。
解决办法
1、 对象传递
1 | class Service { |
这种方法适合于主线程共享的对象而新线程也需要使用,主要适用于 Object
存储公共信息的情景。
2、 新建对象
1 | class Service { |
而这种方法适合于主线程共享的对象对于新线程可有可无的情景,新线程新建一个也不影响原有的业务流程,一般适用于日志标记等。
引用
个人备注
此博客内容均为作者学习所做笔记,侵删!
若转作其他用途,请注明来源!