• 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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    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();
        }
    }
    
    • 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
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    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
    
    • 1
    • 2
  • 相关阅读:
    思维题练习部分
    MiniApp Dev 6
    linux常用命令
    Linux虚拟文件系统管理技术
    Spring security 自定义的AccessDeniedHandler无效,抛出AccessDeniedException 不允许访问
    msql 批量更新生成不同的uuid()
    CobalStrike(CS)流量分析
    AH8691-60V降压至3.3V电源芯片:ESOP8封装解决方案
    10、Redis分布式系统之数据分区算法
    轻松学会JavaScript事件
  • 原文地址:https://blog.csdn.net/dndndnnffj/article/details/134258712