Skip to content
章节导航

ThreadLocal 用法及应用场景

ThreadLocal 的使用场景

线程局部变量(是 Thread 里面的一个局部变量)。 给线程中一个本地变量的副本提供索引,ThreadLocal 可用来维护和当前线程相关的上下文,而且不需要通过每个方法调用将其作为参数进行传递。

ThreadLocal 原理

ThreadLocal 本质上属于以空间换时间,每个 Thread 中,都维护了一个以开放定址法实现的 ThreadLocal.ThreadLocalMap,从而将数据隔离,实现数据不共享,因此就没有线程安全的问题。

四个方法

作用域类型方法描述
publicTget()返回该线程局部变量的当前线程副本中的值
protectedTinitialValue()返回该线程局部变量的当前线程的"初始值"
publicvoidremove()移除该线程局部变量当前线程的值
publicvoidset(T value)将该线程局部变量的当前线程副本中的值设置为指定值

注意事项:

  • 注意内存泄漏问题,使用之后一定要调用 remove()
  • 避免不必要地调用 remove() 加快回收速度
  • initialValue 方法会被多次调用的情况, 例如:显式调用 remove() 后再次 get()
  • Map 中键为线程对象,值为变量副本

ThreadLocal 的用法

  • 定义 UID 随机整数并初始化
  • 通过 ThreadLocal 保存线程私有 ID
  • 每个线程独立维护自己的计数器
  • 线程之间互不影响,保证序列号唯一性
java
public class ThreadLocal {
    private static final AtomicInteger uId = new AtomicInteger(0);

    private static final ThreadLocal<Integer> uNum =
            new ThreadLocal<Integer>() {
                @Override
                protected Integer initialValue() {
                    return uId.getAndIncrement();
                }
            };

    public static int getId() {
        uNum.set(uNum.get() + 1);
        return uNum.get();
    }

    public static void main(String[] args) {
        ThreadLocal uThreadId = new ThreadLocal();

        ThreadTask t1 = new ThreadTask<ThreadLocal>("thread-1", uThreadId);
        ThreadTask t2 = new ThreadTask<ThreadLocal>("thread-2", uThreadId);
        ThreadTask t3 = new ThreadTask<ThreadLocal>("thread-3", uThreadId);
        t1.start();
        t2.start();
        t3.start();
    }
}

InheritableThreadLocal

InheritableThreadLocalMap 的核心就是在创建子线程时,将父线程的 ThreadLocal 值复制到子线程,实现了线程间值的传递,但需要注意内存管理和线程池中的值污染问题。

核心概念

  • ThreadLocal:每个线程都有自己独立的变量副本,线程之间互不干扰。
  • InheritableThreadLocal:允许子线程继承父线程的线程局部变量。

适用场景

  • 传递上下文信息:如 traceId、用户信息等
  • 线程池中的上下文传递(需谨慎处理)
  • 分布式跟踪系统
  • 多级调用链的上下文传递
java
public class InheritableThreadLocalDemo {
    private static final InheritableThreadLocal<String> threadLocal = 
        new InheritableThreadLocal<String>() {
            @Override
            protected String childValue(String parentValue) {
                // 可以对继承的值进行转换
                return "Child inherits: " + parentValue;
            }
        };
    
    public static void main(String[] args) {
        threadLocal.set("Parent Value");
        
        Thread childThread = new Thread(() -> {
            System.out.println("Child thread value: " + threadLocal.get());
            // 子线程修改不影响父线程
            threadLocal.set("Child Modified Value");
            System.out.println("Child thread after modification: " + threadLocal.get());
        });
        
        childThread.start();
        
        try {
            childThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println("Parent thread value after child modification: " + 
                          threadLocal.get()); // 仍然是 "Parent Value"
    }
}
java
public class UniRequestContext {

    private static final ThreadLocal<Map<Object, Object>> resources = new InheritableThreadLocalMap<>();

    //设计一个set方法
    public static void set(Object key, Object value) {
        if (key == null) {
            throw new IllegalArgumentException("key can not be null");
        }
        if (value == null) {
            resources.get().remove(key);
        }
        resources.get().put(key, value);
    }

    public static Long getUserId() {
        Object userId = get("USER_ID");
        return userId == null ? null : (Long) userId;
    }

    //设计一个get方法
    public static Object get(Object key) {
        if (key == null) {
            throw new IllegalArgumentException("key can not be null");
        }
        return resources.get().get(key);
    }

    // 设计一个 clear 方法,防止内存泄漏,springboot-web 容器处理请求,tomcat,工作线程会去处理我们的业务请求,工作线程是会长时间存在的,
    public static void clear() {
        resources.remove();
    }

    // 实现父子线程之间的线程本地变量传递
    // A-->threadLocal ("userId",1001)
    // A-->new Thread(B) -->B 线程属于A线程的子线程,threadLocal get("userId")
    private static final class InheritableThreadLocalMap<T extends Map<Object, Object>> extends InheritableThreadLocal<Map<Object, Object>> {

        @Override
        protected Map<Object, Object> initialValue() {
            return new HashMap();
        }

        @Override
        protected Map<Object, Object> childValue(Map<Object, Object> parentValue) {
            if (parentValue != null) {
                return (Map<Object, Object>) ((HashMap<Object, Object>) parentValue).clone();
            } else {
                return null;
            }
        }
    }

}

最佳实践

  • 及时清理:使用完后调用 remove() 避免内存泄漏
  • 避免存储大对象:ThreadLocalMap 会一直持有引用
  • 线程池谨慎使用:考虑使用 TransmittableThreadLocal
  • 合理设计继承策略:重写 childValue() 方法控制继承行为

解决方案:TransmittableThreadLocal

阿里巴巴的 TransmittableThreadLocal 解决了线程池中的传递问题

java
public class TransmittableThreadLocalDemo {
    private static final TransmittableThreadLocal<String> context = 
        new TransmittableThreadLocal<>();
    
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        
        // 使用 TtlExecutors 包装
        ExecutorService ttlExecutor = TtlExecutors.getTtlExecutorService(executor);
        
        context.set("Request-1");
        ttlExecutor.submit(() -> {
            System.out.println(context.get()); // Request-1
        });
        
        context.set("Request-2");
        ttlExecutor.submit(() -> {
            System.out.println(context.get()); // Request-2
        });
    }
}

内存泄漏问题

java
public class MemoryLeakDemo {
    static class LargeObject {
        private byte[] data = new byte[1024 * 1024]; // 1MB
    }
    
    public static void main(String[] args) {
        ThreadLocal<LargeObject> threadLocal = new ThreadLocal<>();
        threadLocal.set(new LargeObject());
        
        // 使用后必须清理
        threadLocal.remove();
        
        // 或者使用 try-finally 确保清理
        try {
            threadLocal.set(new LargeObject());
            // 业务逻辑
        } finally {
            threadLocal.remove();
        }
    }
}