今天为大家带来的是并发设计模式实战系列,第七章Thread Local Storage (TLS),废话不多说直接开始~
┌───────────────────┐ ┌───────────────────┐
│ Thread 1 │ │ Thread 2 │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ TLS Slot 1 │ │ │ │ TLS Slot 1 │ │
│ ├─────────────┤ │ │ ├─────────────┤ │
│ │ TLS Slot 2 │ │ │ │ TLS Slot 2 │ │
│ └─────────────┘ │ │ └─────────────┘ │
└───────────────────┘ └───────────────────┘
系统组件 | 现实类比 | 核心行为 |
---|---|---|
Thread | 银行客户 | 拥有独立的保险箱使用权 |
TLS | 保险箱系统 | 为每个客户分配独立存储空间 |
get()/set() | 存取操作 | 仅能操作自己的保险箱 |
public class ThreadLocalDemo {
// 创建ThreadLocal实例(支持泛型)
private static final ThreadLocal<SimpleDateFormat> dateFormatHolder =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
public static String formatDate(Date date) {
// 每个线程获取自己独立的SimpleDateFormat实例
return dateFormatHolder.get().format(date);
}
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(3);
// 模拟多线程日期格式化
for (int i = 0; i < 5; i++) {
pool.execute(() -> {
String result = formatDate(new Date());
System.out.println(Thread.currentThread().getName()
+ " => " + result);
});
}
pool.shutdown();
}
}
class UserContextHolder {
private static final ThreadLocal<User> holder = new ThreadLocal<>();
public static void set(User user) {
holder.set(user);
}
public static User get() {
return holder.get();
}
public static void clear() {
holder.remove(); // 防止内存泄漏
}
}
// 在Web过滤器中使用
class AuthFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) {
User user = authenticate((HttpServletRequest) request);
UserContextHolder.set(user); // 设置当前线程用户
try {
chain.doFilter(request, response);
} finally {
UserContextHolder.clear(); // 必须清理!
}
}
}
// 方案1:继承InheritableThreadLocal实现自动清理
class SafeThreadLocal<T> extends InheritableThreadLocal<T> {
@Override
protected void finalize() {
super.remove(); // GC时主动清理
}
}
// 方案2:try-finally标准范式
void businessMethod() {
try {
threadLocal.set(someValue);
// ...业务逻辑
} finally {
threadLocal.remove();
}
}
方案 | 线程安全 | 性能 | 内存开销 | 适用场景 |
---|---|---|---|---|
TLS | 完全安全 | 极高 | 中 | 线程独享对象 |
synchronized | 安全 | 低 | 低 | 少量共享资源 |
ConcurrentHashMap | 安全 | 高 | 高 | 全局共享缓存 |
volatile | 部分安全 | 中 | 低 | 状态标志 |
实现类 | 继承特性 | 适用场景 |
---|---|---|
ThreadLocal | 仅当前线程可见 | 普通线程局部变量 |
InheritableThreadLocal | 子线程可继承 | 线程池需要传递上下文 |
FastThreadLocal (Netty) | 优化版 | 高性能网络框架 |
// 线程池场景下默认会丢失继承关系
ExecutorService pool = Executors.newCachedThreadPool();
InheritableThreadLocal<String> itl = new InheritableThreadLocal<>();
itl.set("parent-value");
pool.execute(() -> {
// 可能获取不到值(线程复用)
System.out.println(itl.get());
});
// 解决方案:自定义线程工厂
class ContextAwareThreadFactory implements ThreadFactory {
private final String context;
public ContextAwareThreadFactory(String ctx) {
this.context = ctx;
}
@Override
public Thread newThread(Runnable r) {
return new Thread(() -> {
itl.set(context);
r.run();
});
}
}
// 对比原生ThreadLocal的改进:
// 1. 使用数组代替哈希表(index预计算)
// 2. 消除哈希冲突处理开销
FastThreadLocal<String> ftl = new FastThreadLocal<>();
ftl.set("netty-optimized");
System.out.println(ftl.get());
// 典型Web应用实现方式
class RequestContextFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain) {
// 绑定请求到当前线程
RequestContextHolder.setRequestAttributes(
new ServletRequestAttributes(request));
try {
filterChain.doFilter(request, response);
} finally {
// 清理线程状态
RequestContextHolder.resetRequestAttributes();
}
}
}
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 服务A │ │ 服务B │ │ 服务C │
│ TLS上下文 │───>│ TLS上下文 │───>│ TLS上下文 │
│ (TraceID) │<───│ (TraceID) │<───│ (TraceID) │
└─────────────┘ └─────────────┘ └─────────────┘
// 使用MDC(Mapped Diagnostic Context)实现
MDC.put("traceId", UUID.randomUUID().toString());
// 通过HTTP Header传播
restTemplate.interceptors.add((request, body, execution) -> {
request.getHeaders().add("X-Trace-ID", MDC.get("traceId"));
return execution.execute(request, body);
});
// 组合ThreadLocal和全局缓存
class HybridContext {
private static final ConcurrentMap<Long, Context> GLOBAL = new ConcurrentHashMap<>();
private static final ThreadLocal<Context> LOCAL = ThreadLocal.withInitial(() -> {
Context ctx = new Context();
GLOBAL.put(Thread.currentThread().getId(), ctx);
return ctx;
});
public static Context get() {
return LOCAL.get();
}
// 允许其他线程有限访问(需谨慎使用)
public static Context get(long threadId) {
return GLOBAL.get(threadId);
}
}
// 使用填充字节保证独立缓存行
class PaddedThreadLocal<T> extends ThreadLocal<T> {
// 每个实例占用128字节(典型缓存行大小)
public long p1, p2, p3, p4, p5, p6, p7 = 0L;
@Override
protected T initialValue() {
return null;
}
public long p8, p9, p10, p11, p12, p13, p14 = 0L;
}
// 复用线程局部对象减少GC
class ObjectPool {
private static final ThreadLocal<LinkedList<Resource>> pool =
ThreadLocal.withInitial(() -> new LinkedList<>());
public static Resource get() {
LinkedList<Resource> list = pool.get();
return list.isEmpty() ? new Resource() : list.removeLast();
}
public static void release(Resource obj) {
obj.reset(); // 重置对象状态
pool.get().add(obj);
}
}
// 通过final修饰促进方法内联
public final class FastContext {
private static final ThreadLocal<FastContext> INSTANCE =
new ThreadLocal<>();
// 内联友好的小方法
public static FastContext get() {
FastContext ctx = INSTANCE.get();
if (ctx == null) {
ctx = new FastContext();
INSTANCE.set(ctx);
}
return ctx;
}
}
反模式 | 后果 | 正确做法 |
---|---|---|
忘记remove() | 内存泄漏 | try-finally中清理 |
存储大对象 | 线程生命周期内存堆积 | 使用WeakReference |
跨线程传递可变对象 | 数据竞争 | 深度拷贝或不可变对象 |
ExecutorService pool = Executors.newFixedThreadPool(4);
// 错误示例:线程复用导致上下文混乱
pool.execute(() -> {
threadLocal.set("job1");
// 可能被其他job复用
});
// 正确方案:每次任务前初始化
pool.execute(() -> {
try {
threadLocal.set(Thread.currentThread().getName());
// 业务逻辑
} finally {
threadLocal.remove();
}
});
// 当ThreadLocal持有ClassLoader引用时
class PluginManager {
static final ThreadLocal<ClassLoader> holder = new ThreadLocal<>();
}
// 解决方案:使用WeakReference
static final ThreadLocal<WeakReference<ClassLoader>> holder =
new ThreadLocal<>();
// 虚拟线程下的TLS行为
Thread.Builder builder = Thread.ofVirtual()
.name("virtual-");
Thread t = builder.start(() -> {
threadLocal.set("value"); // 与传统线程行为一致
});
// 注意:虚拟线程更频繁创建/销毁,需加强内存泄漏防护
# 需要在native-image配置中明确注册
--initialize-at-run-time=com.example.MyThreadLocalClass
// 在Reactor上下文中的桥接
Mono.deferContextual(ctx -> {
// 将TLS值注入响应式上下文
String tlsValue = threadLocal.get();
return Mono.just(tlsValue)
.contextWrite(Context.of("tls", tlsValue));
});
// org.springframework.transaction.support.TransactionSynchronizationManager
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
// 关键设计:
// 1. 使用static final保证单例
// 2. NamedThreadLocal便于诊断
// 3. 完善的clear()机制
监控指标 | 健康阈值 | 工具获取方式 |
---|---|---|
ThreadLocal实例数 | < 线程数×2 | JConsole MBean监控 |
未清理的TLS内存占比 | < 0.1%堆内存 | MemoryAnalyzer工具分析 |
TLS访问耗时 | < 50ns/次 | JMH基准测试 |