本文只对三种变量使用做介绍,实现原理待下篇文章讲解。
1. ThreadLocal的使用
介绍:ThreadLocal是JDK包提供的,它提供线程本地变量,如果创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题,如下图所示
继承关系:
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
public static void main(String[] args) {
CONTEXT_HOLDER.set("initial");
// 线程1
new Thread(() -> {
CONTEXT_HOLDER.set("thread1");
System.out.println("thread1 threadLocal=====" + CONTEXT_HOLDER.get());
}).start();
// 线程2
new Thread(() -> System.out.println("thread2 threadLocal=====" + CONTEXT_HOLDER.get())).start();
// 线程3
new Thread(() -> {
System.out.println("thread3 threadLocal=====" + CONTEXT_HOLDER.get());
}).start();
}
如上代码,线程1对threadlocal变量做了修改,分别输出线程1、线程2、线程3的变量值结果如下:
thread1 threadLocal=====thread1
thread2 threadLocal=====initial
thread3 threadLocal=====initial
由结果可以看出,线程1对ThreadLocal值做的修改,不会影响线程2、线程3的ThreadLocal值,由此可保证线程安全。
问题:
代码中若存在异步操作时,ThreadLocal变量无法传递给子线程
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
public static void main(String[] args) {
// 主线程
CONTEXT_HOLDER.set("main thread");
System.out.println("main thread threadLocal=====" + CONTEXT_HOLDER.get());
// 子线程
new Thread(() -> {
System.out.println("child thread threadLocal=====" + CONTEXT_HOLDER.get());
}).start();
}
如上代码,主线程设置了ThreadLocal变量,分别输出主线程、子线程的ThreadLocal变量值
main thread threadLocal=====main thread
child thread threadLocal=====null
由此可见,的ThreadLocal变量无法由父(主)线程传递给子线程。由此引入InheritableThreadLocal
2. InheritableThreadLocal的使用
InheritableThreadLocal变量主要解决ThreadLocal变量无法进行父子线程传递的问题
private static final InheritableThreadLocal<String> CONTEXT_HOLDER = new InheritableThreadLocal<>();
public static void main(String[] args) {
// 主线程
CONTEXT_HOLDER.set("main thread");
System.out.println("main thread threadLocal=====" + CONTEXT_HOLDER.get());
// 子线程
new Thread(() -> {
System.out.println("child thread threadLocal=====" + CONTEXT_HOLDER.get());
}).start();
}
如上代码,替换成InheritableThreadLocal后,主线程设置ThreadLocal变量,分别输出主线程、子线程的ThreadLocal变量值
main thread threadLocal=====main thread
child thread threadLocal=====main thread
由此可见,InheritableThreadLocal变量可进行父子线程之间的传递
问题:
子线程为新起线程时,InheritableThreadLocal变量值才可传递(具体需见InheritableThreadLocal父子线程的源码),若子线程为线程池复用的线程则无法进行传递
private static final InheritableThreadLocal<String> CONTEXT_HOLDER = new InheritableThreadLocal<>();
// 线程池大小为1 保证后续执行线程为已初始化的线程
private static ExecutorService ioExecutor = new ThreadPoolExecutor(1, 1, 10L, TimeUnit.MINUTES, new ArrayBlockingQueue<>(50), new ThreadPoolExecutor.CallerRunsPolicy());
@Scheduled(fixedDelay = 1000)
public void test() {
CONTEXT_HOLDER.set("main thread");
System.out.println("main thread threadLocal=====" + CONTEXT_HOLDER.get());
// (线程池)子线程
ioExecutor.execute(() -> {
System.out.println("child thread threadLocal=====" + CONTEXT_HOLDER.get());
CONTEXT_HOLDER.set("clear thredlocal");
});
}
定时任务第一次执行输出结果
main thread threadLocal=====main thread
child thread threadLocal=====main thread
定时任务第二次执行输出结果
main thread threadLocal=====main thread
child thread threadLocal=====clear thredlocal
定时任务第三次执行输出结果
main thread threadLocal=====main thread
child thread threadLocal=====clear thredlocal
由上可知,InheritableThreadLocal变量在线程池线程初始化时,会进行变量值的传递,当线程复用时,无法进行变量值的传递
3. TransmittableThreadLocal的使用
需要引入阿里依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.12.1</version>
</dependency>
线程池线程复用场景下,支持父子线程TransmittableThreadLocal变量的传递
private static final TransmittableThreadLocal<String> CONTEXT_HOLDER = new TransmittableThreadLocal<>();
// 线程池大小为1 保证后续线程为已初始化的线程
private static ExecutorService ioExecutor = new ThreadPoolExecutor(1, 1, 10L, TimeUnit.MINUTES, new ArrayBlockingQueue<>(50), new ThreadPoolExecutor.CallerRunsPolicy());
@Scheduled(fixedDelay = 1000)
public void test() {
CONTEXT_HOLDER.set("main thread");
System.out.println("main thread threadLocal=====" + CONTEXT_HOLDER.get());
// (线程池)子线程 注意提交需要使用TtlRunnable
ioExecutor.execute(TtlRunnable.get(() -> {
System.out.println("child thread threadLocal=====" + CONTEXT_HOLDER.get());
CONTEXT_HOLDER.set("clear thredlocal");
}));
}
定时任务第一次执行输出结果
main thread threadLocal=====main thread
child thread threadLocal=====main thread
定时任务第二次执行输出结果
main thread threadLocal=====main thread
child thread threadLocal=====main thread
定时任务第二次执行输出结果
main thread threadLocal=====main thread
child thread threadLocal=====main thread
由结果可知,TransmittableThreadLocal变量在子线程(线程池复用线程)启动时,都会将主线程变量传递至子线程。
总结
threadlocal -> 父子线程变量无法传递
InheritableThreadLocal -> 父子线程会传递(子线程需为新建线程)
TransmittableThreadLocal -> 父子线程会传递(线程池(线程复用)亦可传递)