阿里开源 TransmittableThreadLocal(TTL)教程

一、简介

TransmittableThreadLocal(简称 TTL)是阿里巴巴开源的解决线程池上下文传递 问题的工具。它解决了 InheritableThreadLocal 在线程池场景下无法传递上下文的痛点。

核心问题

  • ThreadLocal:无法在父子线程间传递

  • InheritableThreadLocal:可以在创建时传递,但线程复用场景(线程池)无效

适用场景

  • 链路追踪(TraceId 传递)

  • 用户认证信息传递

  • 配置信息传递

  • 请求上下文传递


二、快速开始

1. Maven 依赖

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

2. 基本使用

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

public class TTLBasicDemo {
    // 创建 TTL 实例
    private static TransmittableThreadLocal<String> ttl = new TransmittableThreadLocal<>();
    
    public static void main(String[] args) {
        // 设置值
        ttl.set("main-value");
        
        // 普通线程能传递
        new Thread(() -> {
            System.out.println("普通线程: " + ttl.get()); // 输出: main-value
        }).start();
        
        // 线程池场景
        ExecutorService executor = Executors.newFixedThreadPool(1);
        
        // 未包装的任务无法传递
        executor.submit(() -> {
            System.out.println("未包装: " + ttl.get()); // 输出: null
        });
        
        // 包装后的任务可以传递
        executor.submit(TtlRunnable.get(() -> {
            System.out.println("TTL包装: " + ttl.get()); // 输出: main-value
        }));
        
        executor.shutdown();
    }
}

三、核心 API

1. TransmittableThreadLocal

java 复制代码
// 创建 TTL
TransmittableThreadLocal<String> ttl = new TransmittableThreadLocal<>();

// 基本操作
ttl.set("value");           // 设置值
String value = ttl.get();   // 获取值
ttl.remove();               // 移除值

// 初始化值
TransmittableThreadLocal<String> ttlWithInit = new TransmittableThreadLocal<String>() {
    @Override
    protected String initialValue() {
        return "default-value";
    }
};

2. 包装工具类

java 复制代码
// Runnable 包装
Runnable wrappedRunnable = TtlRunnable.get(originalRunnable);

// Callable 包装
Callable wrappedCallable = TtlCallable.get(originalCallable);

// 线程池包装
ExecutorService executor = TtlExecutors.getTtlExecutorService(originalExecutor);

四、线程池包装方式

方式一:包装任务(推荐用于临时场景)

java 复制代码
ExecutorService executor = Executors.newFixedThreadPool(5);

// 每次提交时包装
executor.submit(TtlRunnable.get(() -> {
    System.out.println(ttl.get());
}));

方式二:包装线程池(推荐用于全局配置)

java 复制代码
ExecutorService originalExecutor = Executors.newFixedThreadPool(5);
ExecutorService ttlExecutor = TtlExecutors.getTtlExecutorService(originalExecutor);

// 直接提交,自动包装
ttlExecutor.submit(() -> {
    System.out.println(ttl.get()); // 自动传递
});

支持的各种线程池

java 复制代码
// 普通线程池
ExecutorService executor = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(5));

// 定时任务线程池
ScheduledExecutorService scheduledExecutor = TtlExecutors.getTtlScheduledExecutorService(
    Executors.newScheduledThreadPool(5)
);

// ForkJoinPool
ForkJoinPool forkJoinPool = TtlExecutors.getTtlForkJoinPool(new ForkJoinPool(5));

五、实战案例

案例1:链路追踪 TraceId 传递

java 复制代码
public class TraceContext {
    private static TransmittableThreadLocal<String> traceIdHolder = new TransmittableThreadLocal<>();
    
    public static void setTraceId(String traceId) {
        traceIdHolder.set(traceId);
    }
    
    public static String getTraceId() {
        return traceIdHolder.get();
    }
    
    public static void clear() {
        traceIdHolder.remove();
    }
}

// 拦截器设置 TraceId
@Component
public class TraceInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String traceId = UUID.randomUUID().toString();
        TraceContext.setTraceId(traceId);
        return true;
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                                Object handler, Exception ex) {
        TraceContext.clear();
    }
}

// 业务代码中使用
@Service
public class OrderService {
    @Autowired
    private ThreadPoolExecutor threadPool;
    
    public void processOrder() {
        String traceId = TraceContext.getTraceId();
        
        // 使用包装后的线程池,自动传递 TraceId
        threadPool.submit(() -> {
            // 这里能获取到 TraceId
            log.info("处理订单,traceId: {}", TraceContext.getTraceId());
        });
    }
}

案例2:用户上下文传递

java 复制代码
public class UserContext {
    private static TransmittableThreadLocal<UserInfo> userHolder = new TransmittableThreadLocal<>();
    
    public static void setUser(UserInfo user) {
        userHolder.set(user);
    }
    
    public static UserInfo getUser() {
        return userHolder.get();
    }
    
    public static Long getUserId() {
        UserInfo user = getUser();
        return user != null ? user.getId() : null;
    }
}

// 使用示例
@Service
public class AsyncService {
    private ExecutorService executor = TtlExecutors.getTtlExecutorService(
        Executors.newFixedThreadPool(10)
    );
    
    public void asyncProcess() {
        UserInfo currentUser = UserContext.getUser();
        
        executor.submit(() -> {
            // 自动获取当前用户上下文
            UserInfo user = UserContext.getUser();
            System.out.println("处理用户: " + user.getName());
        });
    }
}

案例3:Spring Boot 集成

java 复制代码
@Configuration
public class ThreadPoolConfig {
    
    @Bean
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("async-");
        
        // 包装 ThreadPoolTaskExecutor
        return TtlExecutors.getTtlThreadPoolTaskExecutor(executor);
    }
    
    @Bean
    public ExecutorService customExecutor() {
        ExecutorService executor = Executors.newFixedThreadPool(10);
        return TtlExecutors.getTtlExecutorService(executor);
    }
}

六、高级特性

1. 值拷贝机制

java 复制代码
// 实现 TransmittableThreadLocal.Transmittable 接口自定义拷贝
TransmittableThreadLocal<List<String>> ttl = new TransmittableThreadLocal<List<String>>() {
    @Override
    public List<String> copy(List<String> parentValue) {
        // 深拷贝,避免引用传递导致的数据不一致
        return parentValue != null ? new ArrayList<>(parentValue) : null;
    }
};

2. 禁用 TTL(全局配置)

java 复制代码
// 通过系统属性禁用
System.setProperty("ttl.agent.enabled", "false");

// 或通过环境变量
-Dttl.agent.enabled=false

3. 优雅关闭清理

java 复制代码
@Component
public class TtlCleanupFilter implements Filter {
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                         FilterChain chain) throws IOException, ServletException {
        try {
            chain.doFilter(request, response);
        } finally {
            // 清理所有 TTL 值,防止内存泄漏
            TransmittableThreadLocal.Transmitter.clear();
        }
    }
}

七、性能优化建议

1. 优先包装线程池而非任务

  • 包装线程池性能更好,避免每次任务创建额外对象

2. 及时清理

java 复制代码
try {
    ttl.set(value);
    // 业务逻辑
} finally {
    ttl.remove();  // 重要:避免内存泄漏
}

3. 使用静态变量

java 复制代码
// 推荐:定义为静态变量
private static final TransmittableThreadLocal<String> CONTEXT = new TransmittableThreadLocal<>();

// 不推荐:每次使用时创建
public void method() {
    TransmittableThreadLocal<String> local = new TransmittableThreadLocal<>();
}

八、常见问题与解决方案

1. 值未传递问题

原因:未正确包装线程池或任务

解决

java 复制代码
// 错误
executor.submit(() -> {});

// 正确
executor.submit(TtlRunnable.get(() -> {}));
// 或使用包装后的线程池

2. 内存泄漏

原因 :未调用 remove() 清理

解决

java 复制代码
try {
    ttl.set(value);
    // 业务逻辑
} finally {
    ttl.remove();  // 确保清理
}

3. 嵌套线程池问题

java 复制代码
// 外层线程池
ExecutorService outer = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(5));

outer.submit(() -> {
    // 内层线程池也需要包装
    ExecutorService inner = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(5));
    inner.submit(() -> {
        // 可以正常传递
    });
});

九、对比总结

特性 ThreadLocal InheritableThreadLocal TransmittableThreadLocal
父子线程传递
线程池传递
性能开销
内存泄漏风险 低(需手动清理)
适用场景 线程内隔离 简单父子线程 线程池/异步场景

十、最佳实践总结

  1. 优先包装线程池 :使用 TtlExecutors 包装线程池,避免遗漏任务包装

  2. 及时清理 :在 finally 块中调用 remove() 方法

  3. 静态定义:将 TTL 定义为静态常量,避免重复创建

  4. 深拷贝 :对于可变对象,实现 copy() 方法进行深拷贝

  5. 统一管理:使用拦截器/过滤器统一管理上下文的设置和清理

  6. 监控告警:监控 TTL 的内存占用,设置合理告警

相关推荐
小柯博客2 小时前
STM32MP2安全启动技术深度解析
c语言·c++·stm32·嵌入式硬件·安全·开源·github
Hello__77772 小时前
开源鸿蒙 Flutter 实战|消息通知功能完整实现
flutter·开源·harmonyos
GoCoding2 小时前
开源好物 26/04
开源
结衣结衣.2 小时前
手把手教你实现文档搜索引擎
linux·c++·搜索引擎·开源·c++11
古城小栈3 小时前
hey 你好 “压测”
http·golang·开源
a1117764 小时前
jetpack5.0对应版本的torch和torchvision
python·开源·torch
Hello__77774 小时前
开源鸿蒙 Flutter 实战|仓库评论与点赞功能完整实现
flutter·开源·harmonyos
Teable任意门互动7 小时前
多维表格哪家最好用最容易上手?国产开源 Teable 测评
开发语言·数据库·开源·excel·飞书·开源软件
lularible8 小时前
PTP协议精讲(3.7):传输层实现——PTP报文的“高速公路“
网络·网络协议·开源·嵌入式·ptp
qq_283720058 小时前
本地大模型部署全教程:Python 低成本调用开源 AI 模型
人工智能·python·开源