InheritableThreadLocal从入门到放弃
背景:
一个上线了很久但是请求量很低(平均每天一两次)的历史功能突然出现空指针报错:

我们翻开代码定位到对应的报错代码:

结合堆栈和代码可以确定是由于bdIdJobMap的值为null导致往bdIdEmployeeJobMap这个map中putAll的时候空指针了。
而bdIdJobMap又取自employeeJobMapThread.get(); 那么这个employeeJobMapThread又是何物?

哦豁,employeeJobMapThread居然是个InheritableThreadLocal。
梳理一下报错代码的上下文逻辑如下:
是否和最近的上线有关?
相信大家都有这样的共识:线上出现报错,首先怀疑是否和最近的上线有关系。
我们做的第一件事情也是排查了近期的上线功能,从上线的功能点和相关代码上来看都和这次报错的代码没什么关系,
因此初步排除了这个原因。所以接下来只能进一步了解代码来排查原因了。
要搞清楚当前报错的根因,毫无疑问肯定是要翻过InheritableThreadLocal这座小山啦。
简单聊下InheritableThreadLocal:
提起ThreadLocal,大家应该相对都比较熟悉了,比如存放登录用户信息到ThreadLocal变量中,然后在接口层可以比较方便的获取登录用户,帮助开发提效。
但是对于InheritableThreadLocal,有不少同学都不太了解。
挑重点来说,InheritableThreadLocal相比ThreadLocal多一个能力:在创建子线程Thread时,子线程Thread会自动继承父线程的InheritableThreadLocal信息到子线程中,进而实现在在子线程获取父线程的InheritableThreadLocal值的目的。
举个简单的栗子对比下InheritableThreadLocal和ThreadLocal:
public class InheritableThreadLocalTest {
private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();
private static final InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();
public static void main(String[] args) {
testThreadLocal();
testInheritableThreadLocal();
}
/**
* threadLocal测试
*/
public static void testThreadLocal() {
// 在主线程中设置值到threadLocal
threadLocal.set("我是父线程threadLocal的值");
// 创建一个新线程并启动
new Thread(() -> {
// 在子线程里面无法获取到父线程设置的threadLocal,结果为null
System.out.println("从子线程获取到threadLocal的值: "