TTL 线程间可传递线程本地存储
作用:在新创建的子线程时,希望继承父线程ThreadLocal设置的对象,继续使用。
场景:
- 最典型的 监控或日志系统实现接口调用链中traceid的传递,通过traceid串联整个调用链路的日志。
- 可用于解决多线程不能共享事务的问题。
和InheritableThreadLocal的区别:InheritableThreadLocal 的目的一样,但对线程池的支持不好。实际开发过程中很少手动new Thread的方式创建线程,初始化线程池的的线程为线程池中线程的父线程,现实场景调用线程池的用户线程与 线程池中的线程是没有一点关系的,用户线程调用线程池,一般只是把任务丢到线程池的任务队列(线程池中的线程可能是由其他用户线程创建的,核心线程创建后是一直复用的),所以线程的父子关系可能不成立 ,这样用户线程中的ThreadLocal可能就传递不过去。但TransmittableThreadLocal可以,这就是他们最明示的区别。
技术最快的入门方法就是自己实际使用测试一下:
import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;
import lombok.Data;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TransmittableThreadLocalTest {
// 用ExecutorServiceTtlWrapper 代理 ThreadPoolExecutor
private static ExecutorService executorService =
TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(2));
// TTL
private static ThreadLocal<LocalBean> ttl = new TransmittableThreadLocal();
public static void main(String[] args) {
LocalBean localBean = new LocalBean("init string value");
ttl.set(localBean); // 在主线程设置初始值
new Thread(() -> {
System.out.println("对普通线程的传递性:" + ttl.get());
localBean.setProp("new thread value");
}).start();
// 1、线程池中取值,设置对象内的属性值,测试是否影响外部线程
executorService.execute(() -> {
LocalBean localBean1 = ttl.get();
System.out.println(String.format("TTL(%s): %s", Thread.currentThread().getName(), localBean1.getProp()));
localBean1.setProp(String.format("TTL set in Pool(%s)", Thread.currentThread().getName()));
System.out.println(String.format("After set TTL(%s): %s", Thread.currentThread().getName(), ttl.get().getProp()));
});
sleep(100);
System.out.println(String.format("Main TTL(%s): %s", Thread.currentThread().getName(), ttl.get().getProp()));
// 2、修改对象引用,测试是否影响外部线程
executorService.execute(() -> {
System.out.println(String.format("TTL(%s): %s", Thread.currentThread().getName(), ttl.get().getProp()));
ttl.set(new LocalBean("Modify reference in thread pool"));
});
sleep(100);
System.out.println(String.format("main TTL(%s): %s", Thread.currentThread().getName(), ttl.get()));
}
private static void sleep(long time) {
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Data
static class LocalBean {
private String prop;
LocalBean(String p) {
this.prop = p;
}
}
}
输出结果如下 :
对普通线程的传递性:TransmittableThreadLocalTest.LocalBean(prop=init string value)
TTL(pool-1-thread-1): new thread value
After set TTL(pool-1-thread-1): TTL set in Pool(pool-1-thread-1)
Main TTL(main): TTL set in Pool(pool-1-thread-1)
TTL(pool-1-thread-2): TTL set in Pool(pool-1-thread-1)
main TTL(main): TransmittableThreadLocalTest.LocalBean(prop=TTL set in Pool(pool-1-thread-1))
这张图描述了Thread、InheritableThreadlocal、TransmittableThreadLocal三者的关系,及 他们各自实现线程间ThreadLocal传递的基本原理。