问题
由于线程池的创建参数并不依赖当前线程,而且核心线程不会消失,因此应当将线程与主线程视为无关,所以ThradLocal
处理的Thread.threadLocals
和InheritableThreadLocal
处理的Thread.inheritableThreadLocals
对于线程池都是无效的。
因此为了解决ThreadLocal的传输问题,就不能用InheritableThreadLocal
的思想。
本文的思路是既然不能直接线程相关,那么我们是可以把ThreadLocal中的数据拷贝下来,然后在新线程执行的时候设置到新线程中,这样实现传输,原理如下
首先定义一个Runnable用来封装真正要执行的Runnable
和ThreadLocal
然后在Run的时候用装饰器模式先执行ThreadLocal的设置,然后再执行原本方法,最后再移除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class ExecutorThreadLocalRunnableWrapper<T> implements Runnable { Runnable runnable; ThreadLocal<T> local;
T context;
public ExecutorThreadLocalRunnableWrapper(Runnable runnable, ThreadLocal<T> local) { this.runnable = runnable; this.local = local; this.context = local.get(); }
@Override public void run() { local.set(context); try { runnable.run(); } finally { local.remove(); } } }
|
在线程使用的地方:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @Test public void t2() throws InterruptedException {
ThreadLocal<String> local = new ThreadLocal<>();
ExecutorService executorService = Executors.newFixedThreadPool(2);
for (int i = 0; i < 10; i++) { local.set("" + i); executorService.execute( new ExecutorThreadLocalRunnableWrapper<String>(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"执行真正方法,获取local" + local.get()); } }, local )); } System.out.println("主线程准备结束"); Thread.sleep(1000); }
|
输出结果为
1 2 3 4 5 6 7 8 9 10 11
| 主线程准备结束 pool-1-thread-2执行真正方法,获取local1 pool-1-thread-1执行真正方法,获取local0 pool-1-thread-2执行真正方法,获取local2 pool-1-thread-1执行真正方法,获取local3 pool-1-thread-2执行真正方法,获取local4 pool-1-thread-1执行真正方法,获取local5 pool-1-thread-2执行真正方法,获取local6 pool-1-thread-1执行真正方法,获取local7 pool-1-thread-2执行真正方法,获取local8 pool-1-thread-1执行真正方法,获取local9
|
没有重复的,也说明了设置获取都正确了这样就完成了ThreadLocal的传递
PS: 这里本来想用反射拿到Thread中的ThreadLocalMap,然后再设置ThreadLocalMap,但是测试之后实际上是不行的,会出错,暂时未去找原因
若要有更完美的实现,建议使用alibab开源的transmittable-thread-local
TTL
不需要传ThreadLocal实例等