摘自我球的docs文档,我没时间在CSDN上再写一份,见
问题描述:
在业务的service实现中,有两个接口的方法使用了同一个线程池,在一次log的error报警中,出现了A方法对应的traceId不是A的controller,而是B的controller。原因就是A和B公用了同一个线程池,spring注入的那个
后来写了下面的代码:
import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;
import org.junit.Test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ThreadpoolLogTrace {
@Test
public void threadLocal() throws Exception {
final ThreadLocal<String> parent = new ThreadLocal<>();
parent.set("value-set-in-parent");
System.out.println(parent.get());
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("~~~~~~~");
System.out.println(parent.get());
parent.set("children");
System.out.println(parent.get());
}
};
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(runnable);
TimeUnit.SECONDS.sleep(1);
executorService.submit(runnable);
TimeUnit.SECONDS.sleep(1);
System.out.println("--------");
System.out.println(parent.get());
}
@Test
public void inheritableThreadLocal() throws Exception {
final InheritableThreadLocal<String> parent = new InheritableThreadLocal<>();
parent.set("value-set-in-parent");
System.out.println(parent.get());
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("++++++++");
System.out.println(parent.get());
parent.set("children");
System.out.println(parent.get());
}
};
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(runnable);
TimeUnit.SECONDS.sleep(1);
executorService.submit(runnable);
TimeUnit.SECONDS.sleep(1);
System.out.println("--------");
System.out.println(parent.get());
}
@Test
public void transmittableThreadLocal() throws Exception{
final TransmittableThreadLocal<String> parent = new TransmittableThreadLocal<String>();
parent.set("value-set-in-parent");
System.out.println(parent.get());
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("========");
System.out.println(parent.get());
parent.set("children");
System.out.println(parent.get());
}
};
ExecutorService service = Executors.newFixedThreadPool(1);
ExecutorService executorService = TtlExecutors.getTtlExecutorService(service);
executorService.submit(runnable);
TimeUnit.SECONDS.sleep(1);
executorService.submit(runnable);
TimeUnit.SECONDS.sleep(1);
System.out.println("--------");
System.out.println(parent.get());
}
}
执行的结果:
value-set-in-parent
~~~~~~~
null
children
~~~~~~~
children
children
--------
value-set-in-parent
value-set-in-parent
++++++++
value-set-in-parent
children
++++++++
children
children
--------
value-set-in-parent
value-set-in-parent
========
value-set-in-parent
children
========
value-set-in-parent
children
--------
value-set-in-parent
问题的原因:
我们的线程在执行完毕的时候并没有清除ThreadLocal中的值,导致后面的任务重用现在的localMap。
解决方案:
如果我们能够,在使用完这个线程的时候清除所有的localMap,在submit新任务的时候在重新重父线程中copy所有的Entry。然后重新给当前线程的t.inhertableThreadLocal赋值。这样就能够解决在线程池中每一个新的任务都能够获得父线程中ThreadLocal中的值而不受其他任务的影响,因为在生命周期完成的时候会自动clear所有的数据。Alibaba的一个库解决了这个问题https://github.com/alibaba/transmittable-thread-local/
实现原理:
请看阿里巴巴的wiki,over