TransmittableThreadLocal (TTL)

官方文档

问题描述

在日常的开发中,我们经常会通过多线程来提高业务执行效率,例如:

当前登录用户信息放在ThreadLocal内,然后service在处理业务逻辑时通过线程池来异步的处理,由于线程池内的线程与当前主线程不是同一个,因此获取不到主线程存放的用户信息

Runnable runnable = ()->{
    while(true){
        //处理用户数据-会从ThreadLoca内获取登录人信息
        System.out.println("当前ThreadLocal值为=>"+threadLocal.get());

        try {
            ThreadUtil.sleep(1000);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
};

Runnable runnable2 = ()->{
    //处理订单数据-会从ThreadLoca内获取登录人信息
    System.out.println("当前ThreadLocal值为=>"+threadLocal.get());
};
//异步处理用户数据
CompletableFuture.runAsync(runnable,executor);
//主线程操作
threadLocal.set("hello TTL");

//异步处理订单数据
CompletableFuture.runAsync(runnable2,executor);

JDK为我们提供了 InheritableThreadLocal,但是他只有在创建新线程时才会拷贝(一个新线程只拷贝一次),而线程池内的核心线程是不会销毁的,会处理多个任务,因此就无法获取到当前登录人信息(或者会获取其他人的登录信息)。

这种情况我们就可以使用Alibaba为我们提供的 TransmittableThreadLocal来解决这个问题。

TTL使用

TransmittableThreadLocal继承自InheritableThreadLocal,并扩展了多次拷贝主线程ThreadLocal的功能。

示例:

/**
*打印结果:
	修改为hello TTL
    pool-1-thread-1当前ThreadLocal值为=>0
    pool-1-thread-1当前ThreadLocal值为=>0
    pool-1-thread-1当前ThreadLocal值为=>0
    pool-1-thread-1当前ThreadLocal值为=>hello TTL
    pool-1-thread-1当前ThreadLocal值为=>hello TTL
    pool-1-thread-1当前ThreadLocal值为=>hello TTL
*/
public class TestTTL {
    //创建线程池
    static ExecutorService executor = new ThreadPoolExecutor(
            1,
            1,
            5,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(10),
            new ThreadPoolExecutor.CallerRunsPolicy()
    );
    //使用TransmittableThreadLocal
    static TransmittableThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();

    public static void main(String[] args){

        threadLocal.set("0");

        Runnable runnable = ()->{
            for(int i=0;i<3;i++){
                //处理用户数据
                System.out.println(Thread.currentThread().getName()+"当前ThreadLocal值为=>"+threadLocal.get());
                ThreadUtil.sleep(1000);
            }
        };

        //执行任务1 注意此处需要通过 TtlRunnable.get(runnable)改变runnable类
        CompletableFuture.runAsync(TtlRunnable.get(runnable), executor);

        //主线程 修改 ThreadLocal
        threadLocal.set("hello TTL");
        System.out.println("修改为hello TTL");

        //执行任务2
        CompletableFuture.runAsync(TtlRunnable.get(runnable),executor);

        executor.shutdown();
    }
}

Agent方式无侵入实现

上面的代码中我们使用了 TransmittableThreadLocal,然后在提交Runnable任务时我们需要通过 TtlRunnable.get(runnable) 来修饰Runnable。但是如果我们项目中的代码已经写好了,如果要修改成本很大,此时就可以通过Agent挂载的方式来动态修改Runnable类

方法:启动时配置 javaagent

java -javaagent:C:\Users\gudian\Desktop\test\transmittable-thread-local-2.14.2.jar 
-jar my.jar

你可能感兴趣的:(java,java)