transmittable-thread-local[线程池跨线程值传递]

transmittable-thread-local线程池跨线程值传递

一、先理解核心背景

JDK 原生的 ThreadLocal 仅能在同一个线程 内共享变量;InheritableThreadLocal 虽能将父线程变量传递给子线程,但仅在子线程创建时生效,线程池复用线程时会失效。而 TTL(阿里开源)专门解决线程池场景下的 ThreadLocal 变量传递问题。

二、环境准备(Maven)

首先引入 TTL 依赖(推荐使用最新稳定版):

xml 复制代码
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>transmittable-thread-local</artifactId>
    <version>2.14.2</version>
</dependency>

三、核心使用案例

案例 1:基础使用(父子线程传递)

这是最基础的用法,验证 TTL 能跨线程传递变量(对比 InheritableThreadLocal,TTL 语义更清晰)。

java 复制代码
import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.TtlRunnable;

public class TtlBasicDemo {
    // 定义TTL变量,替代原生ThreadLocal
    private static final TransmittableThreadLocal<String> TTL_CONTEXT = new TransmittableThreadLocal<>();

    public static void main(String[] args) throws InterruptedException {
        // 1. 父线程设置变量
        TTL_CONTEXT.set("Hello, TTL!");
        System.out.println("父线程变量值: " + TTL_CONTEXT.get()); // 输出:Hello, TTL!

        // 2. 创建子线程任务,用TtlRunnable包装(核心:确保变量传递)
        Runnable task = TtlRunnable.get(() -> {
            // 子线程获取父线程的TTL变量
            System.out.println("子线程变量值: " + TTL_CONTEXT.get()); // 输出:Hello, TTL!
            // 子线程修改变量(不会影响父线程)
            TTL_CONTEXT.set("Modified in child");
            System.out.println("子线程修改后的值: " + TTL_CONTEXT.get()); // 输出:Modified in child
        });

        // 3. 启动子线程
        Thread childThread = new Thread(task);
        childThread.start();
        childThread.join(); // 等待子线程执行完成

        // 4. 父线程变量不受子线程修改影响
        System.out.println("父线程最终变量值: " + TTL_CONTEXT.get()); // 输出:Hello, TTL!

        // 5. 释放资源(避免内存泄漏)
        TTL_CONTEXT.remove();
    }
}
案例 2:线程池场景(核心使用场景)

这是 TTL 最常用的场景,解决线程池复用线程时的变量传递问题。两种实现方式

方式 1:包装任务(TtlRunnable/TtlCallable)

适合无法修改线程池创建逻辑的场景(比如使用框架自带的线程池):

java 复制代码
import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.TtlRunnable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TtlThreadPoolTaskWrapDemo {
    private static final TransmittableThreadLocal<String> TTL_CONTEXT = new TransmittableThreadLocal<>();
    // 固定大小的线程池(模拟线程复用)
    private static final ExecutorService THREAD_POOL = Executors.newFixedThreadPool(1);

    public static void main(String[] args) throws InterruptedException {
        // 第一次提交任务
        TTL_CONTEXT.set("任务1的上下文");
        THREAD_POOL.submit(TtlRunnable.get(() -> {
            System.out.println("线程池任务1获取值: " + TTL_CONTEXT.get()); // 输出:任务1的上下文
        })).get();

        // 第二次提交任务(线程池复用同一个线程)
        TTL_CONTEXT.set("任务2的上下文");
        THREAD_POOL.submit(TtlRunnable.get(() -> {
            System.out.println("线程池任务2获取值: " + TTL_CONTEXT.get()); // 输出:任务2的上下文
        })).get();

        // 关闭线程池
        THREAD_POOL.shutdown();
        THREAD_POOL.awaitTermination(1, TimeUnit.SECONDS);
        TTL_CONTEXT.remove();
    }
}
方式 2:修饰线程池(推荐)

适合能自主创建线程池的场景,一次修饰、永久生效,无需每次包装任务:

java 复制代码
import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TtlThreadPoolDecorateDemo {
    private static final TransmittableThreadLocal<String> TTL_CONTEXT = new TransmittableThreadLocal<>();

    public static void main(String[] args) throws InterruptedException {
        // 1. 创建原生线程池,并用TTL修饰(核心步骤)
        ExecutorService originalPool = Executors.newFixedThreadPool(1);
        ExecutorService ttlPool = TtlExecutors.getTtlExecutorService(originalPool);

        // 2. 第一次提交任务(无需包装,修饰后的线程池自动传递变量)
        TTL_CONTEXT.set("任务A的上下文");
        ttlPool.submit(() -> {
            System.out.println("线程池任务A获取值: " + TTL_CONTEXT.get()); // 输出:任务A的上下文
        }).get();

        // 3. 第二次提交任务(线程复用,仍能获取最新变量)
        TTL_CONTEXT.set("任务B的上下文");
        ttlPool.submit(() -> {
            System.out.println("线程池任务B获取值: " + TTL_CONTEXT.get()); // 输出:任务B的上下文
        }).get();

        // 4. 释放资源
        ttlPool.shutdown();
        ttlPool.awaitTermination(1, TimeUnit.SECONDS);
        TTL_CONTEXT.remove();
    }
}

四、关键注意事项

  1. 资源释放 :使用完 TTL 后必须调用 remove() 方法,避免内存泄漏(和 ThreadLocal 一样)。
  2. 线程池修饰优先级:能修饰线程池就优先修饰,避免每次包装任务的重复代码。
  3. 嵌套线程池:如果存在多层线程池嵌套,TTL 依然能正确传递变量(这是它的核心优势)。
  4. 不可变对象:建议 TTL 中存储不可变对象(如 String、Integer),避免多线程修改导致的并发问题。

总结

  1. TTL 核心解决线程池复用线程时 ThreadLocal 变量无法传递的问题,替代 InheritableThreadLocal 处理线程池场景。
  2. 核心使用方式有两种:TtlRunnable/TtlCallable 包装任务、TtlExecutors 修饰线程池(推荐后者)。
  3. 使用后必须调用 remove() 释放 TTL 变量,避免内存泄漏。
相关推荐
To_OC7 小时前
LC 51 N 皇后:我以为难的是回溯,结果栽在了对角线下标
javascript·算法·leetcode
Scott9999HH7 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔7 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
2401_841495647 小时前
【操作系统】进程同步与互斥实验报告
c++·算法·操作系统·进程·并发·同步·互斥
码智社8 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海8 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖9 小时前
JDK 26 新特性详解
java·开发语言
马优晨9 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区11 小时前
怎么把C语言学到精通?
c语言·开发语言