zhihuan祉幻 2025-04-10 16:20 采纳率: 57.1%
浏览 6

TransmittableThreadLocal中使用普通线程池的影响?

TransmittableThreadLocal需要配合TtlRunnable和TtlCallable使用。TtlRunnable和TtlCallable是TransmittableThreadLocal提供的两个包装类,它们分别用于包装Runnable和Callable任务,使得在任务执行时能够传递上下文;或者直接用TtlExecutors包装线程池使用;

但是经过我测试发现 就使用普通的线程池去赋值或获取TransmittableThreadLocal;打印结果也跟使用的一样啊;请问就使用普通的线程池的话影响在哪里呢;


public class TTLTest2 {
    // 声明一个线程池
    private static final ExecutorService pool = Executors.newFixedThreadPool(2);
    // 声明一个TransmittableThreadLocal全局变量
    private static final ThreadLocal<String> tl = new TransmittableThreadLocal<>();

    public static void main(String[] args) {
        // 设置一个值
        tl.set("张三");

        System.out.println(Thread.currentThread().getName() + "--->" + tl.get());
        // 声明一个Runnable对象
        Runnable r1 = () -> {
            System.out.println(Thread.currentThread().getName() + "--->" + tl.get());
        };
        // 使用TtlRunnable包装一下Runnable对象
        TtlRunnable ttlRunnable1 = TtlRunnable.get(r1);
        // 向线程池提交
        pool.submit(ttlRunnable1);
        // 声明一个Callable对象
        Callable<String> c1 = () -> {
            System.out.println(Thread.currentThread().getName() + "--->" + tl.get());
            return "success";
        };
        // 使用TtlCallable包装一下Callable对象
        TtlCallable<String> ttlCallable1 = TtlCallable.get(c1);
        // 向线程池提交
        pool.submit(ttlCallable1);

        tl.set("李四"); // 更新tl中的值
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + "--->" + tl.get());

        // 声明一个Runnable对象
        Runnable r2 = () -> {
            System.out.println(Thread.currentThread().getName() + "--->" + tl.get());
        };
        // 使用TtlRunnable包装一下Runnable对象
        TtlRunnable ttlRunnable2 = TtlRunnable.get(r2);
        // 向线程池提交
        pool.submit(ttlRunnable2);
        // 声明一个Callable对象
        Callable<String> c2 = () -> {
            System.out.println(Thread.currentThread().getName() + "--->" + tl.get());
            return "success";
        };
        // 使用TtlCallable包装一下Callable对象
        TtlCallable<String> ttlCallable2 = TtlCallable.get(c2);
        // 向线程池提交
        pool.submit(ttlCallable2);

        // 关闭线程池
        pool.shutdown();
    }
}

运行结果:

main--->张三
pool-1-thread-1--->张三
pool-1-thread-2--->张三
main--->李四
pool-1-thread-1--->李四
pool-1-thread-2--->李四

将上面的 r1, c1, r2, c2不经过ttl的包装直接提交给线程池执行;结果也是一样的

那么他们差别在哪里??

  • 写回答

3条回答 默认 最新

  • 紫雾凌寒 新星创作者: 人工智能技术领域 2025-04-10 16:21
    关注

    让 “紫雾凌寒” 来帮你回答,本回答结合 DeepSeek

    如果还有问题,请评论或点击头像私信,如果有帮助到您, 请关注、采纳、点赞

    1. 关键点分析:
      - 主要关注点是在使用TransmittableThreadLocal时,配合TtlRunnable和TtlCallable,普通线程池对其会产生怎样的影响。
      - 理解TransmittableThreadLocal的原理以及TtlRunnable和TtlCallable的作用是关键,同时要考虑普通线程池的特性,如线程复用等。
    2. 解决方案:
      - 首先,了解TransmittableThreadLocal是用于在子线程中传递父线程的本地变量。TtlRunnable和TtlCallable是为了确保在使用线程池时,这些本地变量能正确传递。
      - 当使用普通线程池时:
      • 线程池会复用线程。如果在线程池中提交任务时,没有正确处理TransmittableThreadLocal,可能会导致父线程的本地变量在子线程中传递错误或丢失。
      • 例如,假设有如下代码:
    import com.alibaba.ttl.TransmittableThreadLocal;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class TtlThreadLocalExample {
        private static final TransmittableThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();
    
        public static void main(String[] args) {
            threadLocal.set("parent value");
            ExecutorService executorService = Executors.newFixedThreadPool(2);
            executorService.submit(new TtlRunnable(() -> {
                System.out.println("ThreadLocal value in child: " + threadLocal.get());
            }));
            executorService.shutdown();
        }
    }
    
    • 这里,如果没有对线程池进行特殊配置或处理,当线程池中线程复用执行新任务时,可能无法正确获取到父线程中设置的threadLocal值。
    • 为了确保正确传递,需要对线程池进行如下配置:
    import com.alibaba.ttl.TransmittableThreadLocal;
    import com.alibaba.ttl.threadpool.TtlExecutors;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class TtlThreadLocalExample {
        private static final TransmittableThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();
    
        public static void main(String[] args) {
            threadLocal.set("parent value");
            ExecutorService executorService = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(2));
            executorService.submit(new TtlRunnable(() -> {
                System.out.println("ThreadLocal value in child: " + threadLocal.get());
            }));
            executorService.shutdown();
        }
    }
    
    • 通过TtlExecutors.getTtlExecutorService方法对普通线程池进行包装,使得线程池中的线程能够正确传递TransmittableThreadLocal中的值。
      3. 多种解决方案优缺点:
    • 不做特殊处理
      • 缺点:父线程的本地变量无法在子线程中正确传递,会导致业务逻辑错误。例如,可能会使用到错误的上下文数据。
      • 优点:代码简单,不需要额外的配置。但在实际应用中,这种简单方式无法满足需求。
    • 使用TtlExecutors包装线程池
      • 优点:能够确保父线程的TransmittableThreadLocal值在子线程中正确传递,符合业务场景需求,保证了数据的一致性。
      • 缺点:需要引入额外的依赖(TransmittableThreadLocal相关库),并且需要对线程池进行包装,稍微增加了代码的复杂性。
        4. 总结:
    • 在使用TransmittableThreadLocal时,普通线程池如果不进行特殊处理,会导致父线程本地变量在子线程中传递异常。通过使用TtlExecutors对线程池进行包装,可以确保正确传递本地变量,虽然会增加一定复杂性,但能满足业务中跨线程传递上下文数据的需求。

    总之,为了保证TransmittableThreadLocal在普通线程池中的正确使用,使用TtlExecutors包装线程池是必要的操作。

    希望以上解答对您有所帮助。如果您有任何疑问,欢迎在评论区提出。

    评论

报告相同问题?

问题事件

  • 创建了问题 4月10日