ThreadLocal 是由 Java 标准库(JDK)提供的一个工具类。它的作用是为每个线程提供独立的变量副本,使得每个线程都可以独立地修改自己的变量副本,而不会影响到其他线程的变量副本。

ThreadLocal 主要通过一个 ThreadLocal 对象来实现,该对象可以存储任意类型的数据,并且对每个线程都提供一个独立的副本。当一个线程需要访问这个数据时,它会先获取自己的副本,然后在副本上进行操作。这样,即使多个线程同时访问同一个 ThreadLocal 对象,它们也不会互相影响,从而实现了线程间的隔离。

ThreadLocal 的使用非常广泛,特别是在多线程程序中。例如,在 Web 应用程序中,每个请求都会被分配给一个独立的线程来处理,而这些线程之间需要访问共享资源,例如数据库连接、缓存等。使用 ThreadLocal 可以很方便地为每个线程分配一个独立的资源副本,避免了线程间的竞争和同步问题,从而提高了系统的并发性能。

需要注意的是,ThreadLocal 仅仅提供了线程间的隔离,但并不保证线程安全。如果多个线程同时访问同一个可变对象,仍然需要采取其他措施来保证线程安全,例如使用锁或者原子变量等。

正确使用


    1. private static final ThreadLocal<String> currentApp = new ThreadLocal<>();
    请求结束,或者业务结束 remove 掉值
    2. 子线程用 TTL threadLocal 包装传递

种类

在 Java 中,ThreadLocal 主要有两种类型,分别是:

  1. ThreadLocal 类型:这是最常用的类型,它可以存储任意类型的数据,并且对每个线程都提供一个独立的副本。例如:

    ThreadLocal<String> threadLocal = new ThreadLocal<>();
    threadLocal.set("Hello, World!");
    String value = threadLocal.get();
    
  2. InheritableThreadLocal 类型:这是 ThreadLocal 的一个子类,它可以让子线程继承父线程的副本。例如:

    InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();
    inheritableThreadLocal.set("Hello, World!");
       
    new Thread(() -> {
        String value = inheritableThreadLocal.get();
        System.out.println(value); // 输出 "Hello, World!"
    }).start();
    

需要注意的是,因为 ThreadLocal 和 InheritableThreadLocal 都是泛型类,所以在使用时需要指定泛型参数的类型。同时,由于 ThreadLocal 可能会引发内存泄漏问题,因此在使用时需要格外注意,及时清理不再使用的线程副本。

另外,Java 8 中还引入了 ThreadLocalRandom 类,它是一个随机数生成器,每个线程都有自己的种子,可以在多线程环境下生成独立的随机数。这个类虽然也包含 "ThreadLocal" 字样,但它并不是 ThreadLocal 的子类,与 ThreadLocal 类型没有直接关系。

ThreadLocal 内存泄露

  1. 线程持有 ThreadMap,key 是 ThreadLocalRef,value 是具体指
  2. 如果是弱引用,key 被回收后,get(),set(),remove() 调用会处理清除key 为 null 的value值,如果不调用get(),set(),remove()这些方法就是内存泄露。
  3. 如果是强引用private static final,ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,导致Entry内存泄漏。(生产环境目前基本都是这么用的,private static final,然后手动 remove)

内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。