InheritableThreadLocal链路追踪在线程池下问题

摘自我球的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

发布了212 篇原创文章 · 获赞 68 · 访问量 36万+

猜你喜欢

转载自blog.csdn.net/singgel/article/details/100899095