ITL和TTL线程间值的传递
InheritableThreadLocal
InheritableThreadLocal 继承自 ThreadLocal,增加了父线程到子线程的值传递功能。当一个新线程被创建时,InheritableThreadLocal 会将父线程中 ThreadLocal 变量的值拷贝到子线程(浅拷贝),子线程后续对值的修改不会影响到父线程,同时父线程后续对值的修改也不会反映到已经创建的子线程中。ITL在线程池的模式下,就没办法再正确传递。
TransmittableThreadLocal
TransmittableThreadLocal 继承自 InheritableThreadLocal,同样支持父线程到子线程的值传递功能,但在线程池的模式下,可以正确传递,但需要配合TtlExecutors线程池一起使用。
package com.example.demo.test;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class TransmittableThreadLocalTest {// 需要注意的是,使用TTL的时候,要想传递的值不出问题,线程池必须得用TTL加一层代理(下面会讲这样做的目的)
// private static ExecutorService executorService = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(2));private static ExecutorService executorService = Executors.newFixedThreadPool(2);private static ThreadLocal tl = new InheritableThreadLocal<>(); //这里采用TTL的实现public static void main(String[] args) {new Thread(() -> {String mainThreadName = "main_01";tl.set(1);executorService.execute(() -> {sleep(1L);System.out.println(String.format("本地变量改变之前(1), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), tl.get()));});executorService.execute(() -> {sleep(1L);System.out.println(String.format("本地变量改变之前(1), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), tl.get()));});executorService.execute(() -> {sleep(1L);System.out.println(String.format("本地变量改变之前(1), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), tl.get()));});sleep(100L); //确保上面的会在tl.set执行之前执行tl.set(2); // 等上面的线程池第一次启用完了,父线程再给自己赋值executorService.execute(() -> {sleep(1L);System.out.println(String.format("本地变量改变之后(2), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), tl.get()));});executorService.execute(() -> {sleep(1L);System.out.println(String.format("本地变量改变之后(2), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), tl.get()));});executorService.execute(() -> {sleep(1L);System.out.println(String.format("本地变量改变之后(2), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), tl.get()));});System.out.println(String.format("线程名称-%s, 变量值=%s", Thread.currentThread().getName(), tl.get()));}).start();new Thread(() -> {String mainThreadName = "main_02";tl.set(3);executorService.execute(() -> {sleep(1L);System.out.println(String.format("本地变量改变之前(3), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), tl.get()));});executorService.execute(() -> {sleep(1L);System.out.println(String.format("本地变量改变之前(3), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), tl.get()));});executorService.execute(() -> {sleep(1L);System.out.println(String.format("本地变量改变之前(3), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), tl.get()));});sleep(100L); //确保上面的会在tl.set执行之前执行tl.set(4); // 等上面的线程池第一次启用完了,父线程再给自己赋值executorService.execute(() -> {sleep(1L);System.out.println(String.format("本地变量改变之后(4), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), tl.get()));});executorService.execute(() -> {sleep(1L);System.out.println(String.format("本地变量改变之后(4), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), tl.get()));});executorService.execute(() -> {sleep(1L);System.out.println(String.format("本地变量改变之后(4), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), tl.get()));});System.out.println(String.format("线程名称-%s, 变量值=%s", Thread.currentThread().getName(), tl.get()));}).start();}private static void sleep(long time) {try {Thread.sleep(time);} catch (InterruptedException e) {e.printStackTrace();}}
}