快人快语,先说结论,InheritableThreadLocal 是 ThreadLocal 的一个子类,它包含ThreadLocal 的所有功能并且扩展了 ThreadLocal 的功能,允许父线程中的 InheritableThreadLocal 变量的值被子线程继承。这意味着,当创建一个新的线程时,这个新线程可以访问其父线程中 InheritableThreadLocal 变量的值。
先说ThreadLocal
ThreadLocal
是 Java 中的一个类,它提供了一种线程局部(thread-local)变量。这些变量与普通的变量不同,因为每一个访问变量的线程都有其自己的独立初始化的变量副本。ThreadLocal
变量通常被私有 static 字段包含,它们被声明为 ThreadLocal
类型。
以下是 ThreadLocal
的主要特点和作用:
- 线程隔离:
ThreadLocal
为每个线程提供了它自己的变量副本。因此,一个线程不能访问或修改另一个线程的ThreadLocal
变量。这有助于解决多线程环境下的数据共享和同步问题。 - 减少线程间同步:由于每个线程都有自己的
ThreadLocal
变量副本,因此不需要使用同步机制(如synchronized
)来避免并发访问问题。这可以提高多线程程序的性能。 - 方便管理线程上下文信息:
ThreadLocal
可以用来存储和访问线程特定的上下文信息,如用户ID、事务ID等。这样,在程序的不同部分,都可以方便地访问这些线程特定的信息。 - 内存泄漏问题:需要注意的是,
ThreadLocal
可能会导致内存泄漏。当一个线程结束时,它使用的所有ThreadLocal
实例通常不会被垃圾收集器回收,除非显式地删除这些实例。因此,在使用ThreadLocal
时,需要确保在线程结束时正确地清理相关资源。
以下是一个简单的 ThreadLocal
示例:
public class ThreadLocalExample {// 创建一个 ThreadLocal 变量private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();public static void main(String[] args) {// 线程1Thread thread1 = new Thread(() -> {threadLocal.set("Thread 1 data"); // 设置线程1的 ThreadLocal 变量System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get()); // 获取并打印线程1的 ThreadLocal 变量});// 线程2Thread thread2 = new Thread(() -> {threadLocal.set("Thread 2 data"); // 设置线程2的 ThreadLocal 变量System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get()); // 获取并打印线程2的 ThreadLocal 变量});thread1.start();thread2.start();}
}
在这个示例中,我们创建了一个 ThreadLocal<String>
变量,并在两个线程中分别设置了不同的值。每个线程都只能访问和修改它自己的 ThreadLocal
变量副本,因此输出将会是:
Thread-0: Thread 1 data
Thread-1: Thread 2 data
这显示了 ThreadLocal
如何为每个线程提供其自己的变量副本。
注意,上文案例是一个错误的用法,theadLocal变量一定要记得用后即焚,否则会有内存泄漏风险。当线程不再需要时,应该显式地调用ThreadLocal实例的remove()方法,以从当前线程的ThreadLocalMap中移除对应的条目。这样可以确保即使线程对象被回收,其关联的ThreadLocal变量也不会继续占用内存。
我很想从源码的角度讲一讲ThreadLocal的故事,比如ThreadLocal是怎么做到线程隔离的,ThreadLocal和ThreadLocalMap的关系等等,这些东西其实翻一翻ThreadLocal的源码就很快有答案,建议从它的
set
方法开始看起。本文不多做介绍。
InheritableThreadLocal
简单来说,InheritableThreadLocal就是可以继承给子线程的ThreadLocal,ThreadLocal本身并不能继承给子线程,示例如下:
public static void main(String[] args) {//// 线程1Thread thread1 = new Thread(() -> {threadLocal.set("Thread 1 data"); // 设置线程1的 ThreadLocal 变量System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get()); // 获取并打印线程1的 ThreadLocal 变量// 创建子线程 Thread childThread = new Thread(() -> {System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get()); // 子线程尝试获取并打印父线程设置的ThreadLocal 变量的值 });childThread.setName("Child Thread");childThread.start();});// 线程2Thread thread2 = new Thread(() -> {threadLocal.set("Thread 2 data"); // 设置线程2的 ThreadLocal 变量System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get()); // 获取并打印线程2的 ThreadLocal 变量});thread1.start();thread2.start();}
运行结果:
Thread-0: Thread 1 data
Thread-1: Thread 2 data
Child Thread: null
这个案例说明ThreadLocal并不能继承给子线程,那么换成InheritableThreadLocal会如何:
private static final ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();public static void main(String[] args) {// 线程1Thread thread1 = new Thread(() -> {threadLocal.set("Thread 1 data"); // 设置线程1的 ThreadLocal 变量System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get()); // 获取并打印线程1的 ThreadLocal 变量// 创建子线程Thread childThread = new Thread(() -> {System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get()); // 子线程获取并打印父线程设置的 InheritableThreadLocal 变量的值});childThread.setName("Child Thread");childThread.start();});// 线程2Thread thread2 = new Thread(() -> {threadLocal.set("Thread 2 data"); // 设置线程2的 ThreadLocal 变量System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get()); // 获取并打印线程2的 ThreadLocal 变量});thread1.start();thread2.start();}
上面的代码案例仅仅改了一处,就是将threadLocal的对象类型改为了InheritableThreadLocal,其余不变,运行结果如下:
Thread-1: Thread 2 data
Thread-0: Thread 1 data
Child Thread: Thread 1 data
运行结果表示Child Thread
成功拿到了父线程的ThreadLocal变量值,这是怎么做到ThreadLocal传递的?翻开Thread类的构造方法我们一目了然:
private Thread(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {// 获取父线程变量Thread parent = currentThread();// 如果父线程的inheritableThreadLocals有值,则传递给当前线程if (inheritThreadLocals && parent.inheritableThreadLocals != null)this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);}
引申思考
本文的重点来了,如上面源码所示,InheritableThreadLocal是在Thread类的构造方法里面完成值传递的,如果是主线程不是使用new Thread()的方式而是使用了线程池,这时InheritableThreadLocal能够继承给子线程吗?