继承Thread的类中InheritableThreadLocal字段赋值异常问题求助
问题分析:InheritableThreadLocal在Thread子类构造方法中赋值的异常行为
异常现象
当在继承Thread的类的对象创建阶段为InheritableThreadLocal字段设置值时,出现不符合预期的行为:
- 执行该线程的
run()方法时,无法获取到构造方法中设置的值 - 后续创建并运行该类的子类线程时,却能获取到之前父类对象创建时设置的值
复现代码
public class InheritableThreadLocalExample { public static void main(String[] args) { ThreadOne threadOne = new ThreadOne("user-one", "thread-one"); threadOne.start(); ThreadTwo threadTwo = new ThreadTwo("user-two", "thread-two"); threadTwo.start(); } public static class ThreadOne extends Thread { protected static final InheritableThreadLocal<String> USERNAME = new InheritableThreadLocal<>(); public ThreadOne(String username, String threadName) { super(threadName); USERNAME.set(username); } @Override public void run() { System.out.println(Thread.currentThread().getName() + ". Username: " + USERNAME.get()); } } public static class ThreadTwo extends ThreadOne { public ThreadTwo(String username, String threadName) { super(username, threadName); } } }
输出结果
thread-two. Username: user-one thread-one. Username: null
原因解析
1. InheritableThreadLocal的继承时机是线程实例创建时
Java中,创建Thread实例时,会在Thread.init()方法中完成父线程(此处为主线程)的inheritableThreadLocals到新线程的复制操作,这个过程发生在线程对象构造阶段,而非start()调用时。
2. 构造方法的执行线程是主线程,而非新线程本身
ThreadOne和ThreadTwo的构造方法都在主线程中执行:
- 创建
threadOne时,先调用super(threadName)触发Thread初始化,此时主线程的USERNAME还未赋值,所以threadOne线程自身的inheritableThreadLocals中USERNAME的值为null;之后执行USERNAME.set("user-one"),实际是把值存在主线程的inheritableThreadLocals中,和threadOne线程自身的存储无关。 - 当
threadOne.start()后,run()方法调用USERNAME.get(),获取的是threadOne线程自身的inheritableThreadLocals值,自然为null。
3. 静态InheritableThreadLocal是类级别的共享变量
USERNAME被static final修饰,属于ThreadOne类的静态成员,所有ThreadOne和ThreadTwo实例共享同一个InheritableThreadLocal对象,但每个线程的inheritableThreadLocals存储是独立的:
- 创建
threadTwo时,先调用super(threadName)触发Thread初始化,此时主线程的USERNAME已经被threadOne的构造方法设置为user-one,所以threadTwo线程自身的inheritableThreadLocals会复制这个值;之后执行USERNAME.set("user-two"),只是更新了主线程的inheritableThreadLocals值,不会影响已经复制到threadTwo线程中的值。 - 当
threadTwo.start()后,run()方法获取的是threadTwo线程自身复制的user-one,因此输出该值。
内容的提问来源于stack exchange,提问作者bepopov




