ThreadLocal之父子线程传值

不同的传值场景

ThreadLocal是线程私有变量,每个线程均有一个私有的ThreadLocal变量,为在复杂场景下的传值提供了一种便捷的方式。

比如

ThreadLocal<String> threadLocal = new ThreadLocal<>();
...some codes...
threadLocal.set("value:::init-set");
...some codes...
threadLocal.get();

但是ThreadLocal只能解决在某一个线程内的变量set/get,假如现在我们想在子线程中获取父线程中的ThreadLoca变量的值,怎么办?

什么是父子线程

在A线程中,创建并启动了线程B,那么A就是B的父线程,B就是A的子线程;

对某线程而言,至多有一个父线程,可以有多个子线程。

public static void main(String[] args) {
        //latch仅仅为了控制程序运行顺序,与主题无关
        CountDownLatch latch = new CountDownLatch(1);

        ThreadLocal<String> local = new ThreadLocal<>();
        local.set("value:::value-in-prent");
        System.out.println("[1]" + local.get());

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("[2]" + local.get());
                local.set("value:::value-in-child");
                System.out.println("[3]" + local.get());
                latch.countDown();
            }
        }).start();

        try {
            latch.await();
            System.out.println("[4]" + local.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }        
}
//输出如下:
[1]value:::value-in-parent
[2]null                   
[3]value:::value-in-child
[4]value:::value-in-parent

我们期望在[2]处输出value:::value-in-parent,显然ThreadLocal无法满足需求。JDK中提供了InheritableThreadLocal,将上面代码中的ThreadLocal替换为InheritableThreadLocal,其他内容保持不变:

可以看到,主线程中的local变量的值被传递到了子线程中,这就解决了无法在子线程中获取父线程ThreadLocal值的问题。此外,上面的代码中标出的(*)位置处,改变了子线程中local值,但父线程中的local值没有改变,这就是说,子线程仅仅是获取了父线程中local值作为自己的初始值,但子线程改变自己的local值,并不会影响父线程的local值。

但是,在实际项目中,我们一般不会直接通过new Thread这种方式使用线程,而是使用线程池,这样就会对线程重复利用。此时,假如向线程池提交多个任务,这些任务会改变所在线程的threadlocal值,但是新提交的任务又必须使用父线程中的值,怎么办?我们还是使用Inheritable来试一下,看它能不能满足需求:

我们期望[4]处获取的值为value:::value-in-parent,但实际却是value:::value-in-runnable1,这是因为,我们的线程池中只有一个线程(假设为A),runnable1执行完毕之后,将A线程的local值设为了value:::value-in-runnable1,runnable2仍然是在A线程执行,那么[4]处就是runnable1改变之后的local值。

该问题的解决方案为阿里开源的Transmittable ThreadLocal(TTL),上述问题的解决代码如下:

可见,上面的[4]处输出的值为父线程中的local值,满足了我们的需求。

参考

ThreadLocal父子线程传递实现方案

Github:Transmittable ThreadLocal(TTL)

Last updated