likes
comments
collection
share

为什么ThreadLocal使用频率如此之高?

作者站长头像
站长
· 阅读数 24
  • ThreadLocal是什么?

    ThreadLocal 是 Java 中的一个类,它提供了线程局部变量的存储。使用 ThreadLocal 可以创建线程局部变量,这些变量是线程安全的,并且每个线程都拥有自己的独立副本,不会与其他线程共享。

    下面是一个简单的 ThreadLocal 使用示例:

    public class ThreadLocalExample {
        private static final ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
            @Override
            protected Integer initialValue() {
                return 0; // 提供一个初始值
            }
        };
    
        public static void main(String[] args) {
            new IncrementThread().start();
            new IncrementThread().start();
        }
    
        static class IncrementThread extends Thread {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    int value = threadLocal.get();
                    threadLocal.set(value + 1);
                    System.out.println(Thread.currentThread().getName() + " : " + value);
                }
            }
        }
    }
    

    在这个例子中,每个线程都会独立地对 ThreadLocal 中的值进行操作,互不影响。

  • ThreadLocal应用场景

    ThreadLocal 在 Java 多线程编程中有多种应用场景,以下是一些常见的使用情况:

    1. 线程局部变量存储ThreadLocal 可以用来存储线程内部的局部变量,使得同一个线程的不同函数或组件能够访问到相同的变量,而无需在函数之间传递该变量,从而减少了参数传递的复杂性16。
    2. 解决线程安全问题:在高并发场景下,多个线程可能会同时修改一个共享变量,使用 ThreadLocal 可以避免这种情况,因为每个线程都会有变量的独立副本,从而避免了线程间的数据竞争。
    3. 代替显式参数传递:在一些复杂的业务逻辑中,可以将一些参数存储在 ThreadLocal 中,避免在多层函数调用中显式传递这些参数。
    4. 全局存储用户信息:在前后端分离的系统中,用户信息通常存储在 Session 或 Token 中。使用 ThreadLocal 可以在拦截器中获取用户信息并存储,之后在需要的地方通过 ThreadLocal 获取,简化了用户信息的获取过程。
    5. 管理线程私有的数据库连接:在 Spring 等框架中,可以通过 ThreadLocal 为每个线程管理自己的数据库连接,避免了多线程环境下的数据库连接冲突问题。
    6. 工具类对象的线程局部化:一些工具类对象,如 SimpleDateFormatRandom,不适合在多线程中共享使用。使用 ThreadLocal 可以为每个线程创建这些工具类的实例,避免线程安全问题。
    7. 计数器:可以使用 ThreadLocal 作为线程安全的计数器,每个线程可以独立地对自己的计数器进行操作,互不影响。
    8. 与特定线程相关的状态信息:在网络编程中,如 Netty,ThreadLocal 可以用来存储与特定 Channel 相关的状态信息,因为 FastThreadLocalThreadLocal 的一个变种,它与 Netty 的线程模型结合使用,提供了更高效的线程局部变量存储。

    需要注意的是,ThreadLocal 不适用于线程池等场景,因为线程池中的线程是可复用的,如果 ThreadLocal 使用不当,可能会导致内存泄漏。此外,使用完 ThreadLocal 后,应该调用 remove() 方法来清除数据,防止内存泄漏。

  • ThreadLocal原理解析

    核心方法

    1. set(T value) :为当前线程设置一个线程局部变量的值。
    2. get() :获取当前线程所对应的线程局部变量。
    3. remove() :移除当前线程绑定的局部变量,这通常在线程结束时调用,以避免内存泄漏。

    实现原理

    • set 方法:将当前线程的 Thread 对象与 ThreadLocal 变量的值关联起来。ThreadLocalMap 作为 Thread 对象的一个属性,以 ThreadLocal 实例为键,以变量的值为值进行存储。
    • get 方法:从当前线程的 ThreadLocalMap 中获取与调用 ThreadLocal 实例关联的值。
    • initialValue 方法:返回该线程局部变量的初始值,这个方法是一个受保护的方法,可以被子类覆盖以提供不同的初始值。
    • remove 方法:从 ThreadLocalMap 中移除当前 ThreadLocal 实例对应的条目。

    源码分析

    以下是 ThreadLocal 的简化版源码分析:

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
    
    public void remove() {
        ThreadLocalMap m = getMap(Thread.currentThread());
        if (m != null)
            m.remove(this);
    }
    
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    

    在这个简化的源码中:

    • set() 方法首先获取当前线程对象,然后获取或创建 ThreadLocalMap,最后将 ThreadLocal 实例和值存入该映射。
    • get() 方法同样获取当前线程对象和 ThreadLocalMap,然后尝试找到与当前 ThreadLocal 关联的条目,并返回其值。如果没有找到,则调用 setInitialValue() 方法来设置初始值。
    • remove() 方法移除当前线程的 ThreadLocalMap 中与 ThreadLocal 实例关联的条目。
    • ThreadLocalMap 是一个特殊的 HashMap,它使用 ThreadLocal 实例作为键,而线程局部变量的值作为值。

    注意事项

    • 由于 ThreadLocal 存储的是线程的局部变量,因此它不适用于线程池等多线程环境中的线程复用场景,否则可能导致内存泄漏。

    • 应当在线程结束时调用 remove() 方法,以清除 ThreadLocalMap 中的条目,避免内存泄漏。

    • ThreadLocal 适合用于保存不需要在线程间共享的数据,如线程的局部计数器、用户信息等。

  • ThreadLocal的内存泄露

    ThreadLocal 导致的内存泄漏主要可以归纳为以下三种场景:

    1. 线程池中的线程复用:在使用线程池的情况下,线程会重复执行不同的任务,而不会结束。如果 ThreadLocal 变量用于这些线程,并且没有在任务结束时通过 remove() 方法清除,那么 ThreadLocalMap 中的 Entry 对象将保留对原始值的强引用,导致内存泄漏。
    2. 静态 ThreadLocal 实例:当 ThreadLocal 被声明为静态变量时,它的生命周期与应用程序相同,这意味着存储在 ThreadLocal 中的对象将一直存在,直到应用程序结束,从而增加了内存泄漏的风险。
    3. 未及时移除 ThreadLocal:在线程执行完毕后,如果未调用 ThreadLocalremove() 方法来移除 ThreadLocalMap 中的条目,那么即使 ThreadLocal 对象本身被垃圾回收,其值仍然会被 ThreadLocalMap 强引用,无法被回收,导致内存泄漏。

    ThreadLocal 为了防止内存泄露,采取了以下措施:

    1. 使用弱引用作为键(Key)ThreadLocalMap 中的键是 ThreadLocal 实例的弱引用。当 ThreadLocal 实例没有外部强引用时,它将被垃圾回收器回收,这有助于减少内存泄露的风险,因为一旦 ThreadLocal 对象被回收,对应 ThreadLocalMap 中的条目最终将变成孤立的条目,可以被清理。
    2. 提供 remove() 方法ThreadLocal 提供了一个 remove() 方法,允许开发者在不再需要某个线程局部变量时显式地移除它。这会从 ThreadLocalMap 中删除对应的条目,从而释放值对象,避免内存泄露。
    3. 清理过期条目:在 ThreadLocalMapset()get()remove() 方法中,一旦检测到键(Key)为 null 的条目,ThreadLocalMap 会进行清理,移除这些过期的条目,以避免内存泄露。
    4. expungeStaleEntry() 方法:这是 ThreadLocalMap 中用于清理单个过期条目(即键已经被垃圾回收的条目)的方法。它不仅清除了过期条目的值,而且还尝试重新分配其他有效条目,填补由于删除过期条目而留下的空白。
    5. cleanSomeSlots() 方法:这个方法在 ThreadLocalMap 进行插入操作时被调用,尝试清理一定数量的过期条目,以维持 ThreadLocalMap 的清洁。
    6. rehash() 方法:在必要时,ThreadLocalMap 会重新计算哈希表的大小,并重新分配所有条目到新的哈希表中。在这个过程中,过期的条目会被排除在外,从而实现清理。
    7. 避免在线程池中使用:由于线程池中的线程会长时间存活并被复用,推荐在使用线程池时格外小心使用 ThreadLocal,或者在任务执行完毕后及时清理 ThreadLocal 变量。
    8. 建议最佳实践:建议将 ThreadLocal 变量声明为 private static,这样可以保证 ThreadLocal 实例的生命周期受控,并且可以在任何时候通过其弱引用来访问和清除 Entry 的值。
  • ThreadLocal的变种

    • InheritableThreadLocal

      InheritableThreadLocal 是 Java 中的一个类,它继承自 ThreadLocal 类。正如其名称所示,InheritableThreadLocal 提供了一种机制,允许子线程访问父线程中设置的局部变量,即实现了线程局部变量的继承。这在多线程环境中非常有用,尤其是在需要在父线程和子线程之间传递数据时。

      以下是 InheritableThreadLocal 的一些关键特性:

      1. 继承性InheritableThreadLocal 的关键特性是它允许子线程继承父线程中的数据。这是通过在子线程创建时复制父线程的 InheritableThreadLocal 值来实现的。

      2. 线程隔离:尽管子线程可以访问父线程的 InheritableThreadLocal 值,但是每个线程仍然拥有自己的变量副本,互不影响,从而保持了线程间的隔离性。

      3. 使用场景InheritableThreadLocal 常用于如下场景:

        • 需要在线程及其子线程间传递数据,如用户身份验证信息、事务信息、日志记录上下文等。
        • 服务器端处理请求时,请求上下文信息需要传递给该请求生成的任何异步处理任务。
      4. 内存管理:与 ThreadLocal 类似,为了避免内存泄漏,应当在适当的时候调用 InheritableThreadLocalremove() 方法来清除不再需要的局部变量。

      5. 线程池中的使用:在线程池中,由于线程是可复用的,InheritableThreadLocal 的继承特性不会自动生效。因此,如果需要在线程池中使用 InheritableThreadLocal,可能需要额外的逻辑来显式传递值。

      下面是一个简单的 InheritableThreadLocal 使用示例:

      public class InheritableThreadLocalExample {
      
          private static final InheritableThreadLocal<String> ITL = new InheritableThreadLocal<>();
      
          public static void main(String[] args) {
              ITL.set("Parent Value");
      
              new Thread(() -> {
                  String value = ITL.get(); // 应该能够获取到 "Parent Value"
                  System.out.println("Value in child thread: " + value);
              }).start();
          }
      }
      

      以下是 InheritableThreadLocal 继承父线程变量副本的关键源码分析:

    1. ThreadLocalMap 的使用InheritableThreadLocal 使用了 ThreadLocalMap 来存储每个线程的局部变量副本。但是,与 ThreadLocal 相比,InheritableThreadLocal 使用了线程的 inheritableThreadLocals 字段而不是 threadLocals 字段。
      1. childValue(T parentValue) 方法:此方法允许子线程获取父线程中 InheritableThreadLocal 变量的值。如果没有重写此方法,子线程将直接继承父线程的值。

        protected T childValue(T parentValue) {
            return parentValue;
        }
        
      2. getMap(Thread t) 和 createMap(Thread t, T firstValue) 方法的重写InheritableThreadLocal 重写了这两个方法,以便操作 inheritableThreadLocals 而不是 threadLocals

        ThreadLocalMap getMap(Thread t) {
            return t.inheritableThreadLocals;
        }
        
        void createMap(Thread t, T firstValue) {
            t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
        }
        
      3. Thread 类的 init() 方法:在 Thread 类的构造函数中调用的 init() 方法负责初始化线程的 inheritableThreadLocals。如果 inheritThreadLocals 参数为 true 且父线程的 inheritableThreadLocals 不为 null,则子线程将获得父线程 inheritableThreadLocals 的副本。

        private void init(ThreadGroup g, Runnable target, String name, long stackSize,
                          AccessControlContext acc, boolean inheritThreadLocals) {
            // ...
            Thread parent = currentThread();
            if (inheritThreadLocals && parent.inheritableThreadLocals != null)
                this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
            // ...
        }
        
      4. ThreadLocal.createInheritedMap(ThreadLocalMap parentMap) 静态方法:此方法用于创建一个从父线程的 inheritableThreadLocals 继承而来的 ThreadLocalMap

        static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
            return new ThreadLocalMap(parentMap);
        }
        
      5. 清理:为了避免内存泄漏,应当在适当的时候调用 InheritableThreadLocal 的 remove() 方法来清除不再需要的局部变量。

    • TransmittableThreadLocal

      TransmittableThreadLocal 是由 Alipay(支付宝,阿里巴巴集团的金融服务部门)开源的一个 Java 工具类,它是 InheritableThreadLocal 的一个增强版本,旨在解决线程池等线程复用场景下线程局部变量的传递问题。 在标准的 Java 中,InheritableThreadLocal 允许父线程的变量值传递给子线程,这在新线程由父线程直接创建时是有用的。但是,在线程池这种线程复用的环境中,线程的创建和使用是分离的,因此 InheritableThreadLocal 无法满足在任务提交时传递 ThreadLocal 值到任务执行时的需求。 TransmittableThreadLocal 的主要特点包括:

    1. 跨线程传递:它支持在提交任务到线程池时捕获 ThreadLocal 值,并在任务执行时恢复这些值,从而实现跨线程传递。
    2. 无侵入:它可以通过 Java Agent 技术无侵入地修改 JDK 线程池实现,使得业务代码无需关心线程局部变量的传递。
    3. 简单易用:提供了简洁的 API 来设置、获取和移除变量,同时提供了工具类 TtlRunnable 和 TtlCallable 来包装任务,以便在线程池中使用。
    4. 透明执行:通过重写 childValue 方法,可以在子线程中定制变量值的传递行为,而不是简单地继承父线程的值。
    5. 生命周期管理:提供了 beforeExecute 和 afterExecute 等生命周期回调方法,允许在任务执行前后进行额外的处理。 TransmittableThreadLocal 是中间件和框架开发者的一个有用工具,它解决了多线程环境中的上下文传递问题,特别适用于需要在异步任务或分布式系统中保持上下文信息的场景。

    TransmittableThreadLocal(TTL)的实现原理涉及到几个关键的类和方法,以下是对这些关键组件的源码分析:

    1. TransmittableThreadLocal 类的实现TransmittableThreadLocal 继承自 InheritableThreadLocal,重写了 childValue 方法来定制子线程中变量值的传递行为。它使用一个静态的 WeakHashMap 来持有 TransmittableThreadLocal 实例的引用。
    2. holder 静态变量holder 是一个 InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, Object>>,用来存储 TransmittableThreadLocal 实例和它们的值。使用 WeakHashMap 是为了确保当 TransmittableThreadLocal 实例没有其他强引用时,它可以被垃圾回收,防止内存泄漏。
    3. addThisToHolder 方法: 当 TransmittableThreadLocal 被设置时,通过 addThisToHolder 方法将当前实例添加到 holder 中。
    4. Transmitter 内部类的实现Transmitter 内部类提供 capturereplayrestore 三个静态方法,用于处理 TransmittableThreadLocal 值的捕获、回放和恢复。
    5. capture 方法capture 方法遍历所有 TransmittableThreadLocal 实例,并将它们的值存储到 Snapshot 对象中。
    6. replay 方法replay 方法接收一个 Snapshot 对象,将存储的值回放到当前线程的 TransmittableThreadLocal 中,并返回一个包含当前线程状态的 Snapshot 对象。
    7. restore 方法restore 方法使用 replay 方法返回的 Snapshot 对象来恢复线程的状态。
    8. TtlRunnableTtlCallable: 这些类包装了提交给线程池的任务,确保在任务执行前,父线程的 TransmittableThreadLocal 值被传递到子线程。
    9. Java Agent 技术: TTL 支持通过 Java Agent 技术无侵入地修改 JDK 线程池实现,这是通过字节码增强实现的,可以在不修改线程池代码的情况下实现 TransmittableThreadLocal 的传递。

    以下是 TransmittableThreadLocal 和相关类的简化示例代码:

    public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T> {
        // ... 省略其他代码 ...
    
        private static final InheritableThreadLocal<Map<TransmittableThreadLocal<?>, Object>> holder =
            new InheritableThreadLocal<>();
    
        @Override
        protected T childValue(T parentValue) {
            // 子线程获取父线程的值
            return parentValue;
        }
    
        public void set(T value) {
            super.set(value);
            // 添加到 holder 中
            addThisToHolder();
        }
    
        private void addThisToHolder() {
            Map<TransmittableThreadLocal<?>, Object> map = holder.get();
            if (map == null) {
                map = new HashMap<>();
                holder.set(map);
            }
            map.put(this, null);
        }
    
        // ... 省略其他代码 ...
    }
    
    public static class Transmitter {
        public static Object capture() {
            // 创建 Snapshot 对象,捕获当前线程的所有 TTL 值
            return new Snapshot(captureTtlValues(), captureThreadLocalValues());
        }
    
        public static Object replay(Object captured) {
            // 回放之前捕获的 TTL 值
            return new Snapshot(replayTtlValues(captured), replayThreadLocalValues(captured));
        }
    
        public static void restore(Object backup) {
            // 恢复之前备份的 TTL 值
            restoreTtlValues((Snapshot) backup);
        }
    
        // ... 省略其他代码 ...
    }
    
    final static class Snapshot {
        private final Map<TransmittableThreadLocal<?>, Object> ttl2Value;
        // ... 省略其他代码 ...
    }
    
    // 使用 TransmittableThreadLocal 的示例
    TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();
    context.set("parentValue");
    Object captured = TransmittableThreadLocal.Transmitter.capture();
    // 执行线程池任务
    TransmittableThreadLocal.Transmitter.replay(captured);
    try {
        // 执行业务逻辑
    } finally {
        // 恢复之前的 TransmittableThreadLocal 值
        TransmittableThreadLocal.Transmitter.restore(captured);
    }
    

    在实际使用中,开发者需要确保在任务执行完毕后调用 restore 方法来清理和恢复线程状态,避免影响线程池中其他任务的执行。

    • FastThreadLocal

    FastThreadLocal 是 Netty 框架中的一个类,它是一个特殊的 ThreadLocal 变体,旨在提供比 JDK 自带的 ThreadLocal 更高的访问性能。FastThreadLocal 通过使用数组和固定索引的方式,而不是 JDK ThreadLocal 中的哈希表,从而减少了哈希冲突和提高了访问速度。这种方式特别适合于高并发场景下频繁访问线程局部变量的情况。

    以下是 FastThreadLocal 的一些关键特点:

    1. 性能优势:由于内部使用数组和固定索引,FastThreadLocal 可以提供接近常数时间复杂度 O(1) 的读写操作,这比 JDK 的 ThreadLocal 快,后者可能因为哈希冲突而导致性能下降。
    2. 内存池化FastThreadLocal 使用内存池来减少内存占用和性能开销,通过重用线程局部变量的实例来避免频繁的创建和销毁操作。
    3. 安全清理FastThreadLocal 提供了 remove() 方法来主动清除不再使用的线程局部变量,并且在线程执行完毕时会自动清理所有绑定在当前线程上的所有 FastThreadLocal 对象,减少了内存泄漏的风险。
    4. 使用条件:为了充分利用 FastThreadLocal 的性能优势,访问它的线程必须是 FastThreadLocalThread 或其子类。Netty 通过 DefaultThreadFactory 创建的线程默认就是 FastThreadLocalThread 类型。
    5. 适用场景FastThreadLocal 适用于那些需要频繁读写线程局部变量的场景,尤其是在网络服务器和客户端等高性能网络应用中。
    6. 字节填充FastThreadLocal 利用字节填充来解决伪共享问题,提高缓存效率。
    7. 测试结果:在 Netty 的测试中,FastThreadLocal 的吞吐量可以达到 JDK 原生 ThreadLocal 的 3 倍左右,但在普通线程中使用可能更慢。
    8. 注意:从 Netty 的某个版本开始,FastThreadLocal 不再使用 ObjectCleaner 处理内存泄漏,而是建议在必要时重写 onRemoval 方法。

    FastThreadLocal 之所以比 JDK 的 ThreadLocal 快,主要原因如下:

    1. 数组和固定索引FastThreadLocal 使用一个数组 indexedVariables 和每个 FastThreadLocal 实例的固定索引来存储和访问线程局部变量,这种方法减少了哈希冲突的可能性,并且访问速度更快。
    2. 减少内存开销:由于避免了哈希表的使用,FastThreadLocal 减少了内存开销,因为它不需要为每个 ThreadLocal 对象分配哈希表条目。
    3. 内存池化FastThreadLocal 可以使用内存池技术来减少内存分配和垃圾收集的开销。
    4. 自动清理:在 FastThreadLocalThread 执行完��后,FastThreadLocal 会自动清理所有绑定在当前线程上的所有 FastThreadLocal 变量,减少了内存泄漏的风险。
    5. 避免伪共享FastThreadLocal 使用内存填充(padding)来避免 CPU 缓存伪共享问题。

    具体源码分析

    以下是 FastThreadLocal 的一些关键源码片段,展示了它是如何实现上述功能的:

    1. FastThreadLocal 的构造函数:为每个 FastThreadLocal 实例分配一个唯一的索引。
    public class FastThreadLocal<T> {
        final int index; // 每个 FastThreadLocal 实例都有一个唯一的索引
        final InternalThreadLocalMap<T> map; // 持有 InternalThreadLocalMap 的引用
    
        public FastThreadLocal() {
            index = InternalThreadLocalMap.nextVariableIndex(); // 分配一个索引
            map = InternalThreadLocalMap.get(); // 获取当前线程的 InternalThreadLocalMap
        }
        // ...
    }
    
    1. InternalThreadLocalMap 的 get() 方法:获取当前线程的 InternalThreadLocalMap 实例。
    public final class InternalThreadLocalMap<T> {
        // 存储所有 FastThreadLocal 变量的数组
        static final Object UNSET = new Object();
        private Object[] indexedVariables = newIndexedVariableTable();
    
        public static <V> InternalThreadLocalMap<V> get() {
            Thread currentThread = Thread.currentThread();
            if (currentThread instanceof FastThreadLocalThread) {
                return ((FastThreadLocalThread) currentThread).threadLocalMap();
            } else {
                return slowGet(); // 非 FastThreadLocalThread 线程的回退逻辑
            }
        }
    
        private static Object[] newIndexedVariableTable() {
            Object[] array = new Object[32];
            for (int i = 0; i < array.length; i++) {
                array[i] = UNSET;
            }
            return array;
        }
        // ...
    }
    
    1. FastThreadLocal 的 get() 和 set() 方法:通过索引快速获取和设置线程局部变量的值。
    public final T get() {
        Object[] variables = map.indexedVariables;
        return (T) variables[index];
    }
    
    public final void set(T value) {
        Object[] variables = map.indexedVariables;
        variables[index] = value;
    }
    
    1. 避免伪共享FastThreadLocal 对象通过内存填充来确保每个对象的大小超过 CPU 缓存行的大小,从而避免伪共享。
    // 伪共享的填充示例(具体实现可能会有所不同)
    public class FastThreadLocal<T> {
        // ...
        // 填充字段,以确保对象大小超过缓存行大小
        private long p0, p1, p2, p3, p4, p5, p6, p7;
        // ...
    }
    
    1. remove() 方法:移除线程局部变量,减少内存泄漏的风险。
    public final void remove() {
        map.remove(index);
    }
    

    通过上述源码分析,我们可以看到 FastThreadLocal 在设计上避免了 ThreadLocal 的一些性能瓶颈,如哈希冲突和锁竞争,同时通过内存池化和自动清理机制,提高了性能并减少了内存泄漏的风险。此外,通过内存填充避免伪共享,进一步提高了缓存效率。

  • 总结

    • ThreadLocal是JDK自带的实现,用于提供线程局部变量存储,适用于在单个线程内传递数据的场景。ThreadLocal无法满足在子线程间进行数据传递,而往往现在的应用服务都会出现在父子线程的情况,因此要注意评估。同时,要注意评估内存泄露的问题。内存泄露大多数发生在不被销毁的线程、复用的线程,例如线程池的复用线程,虽然key是虚引用,但是value值还是强引用,虽然会通过get、set、remove进行主动探测清理,但仍需评估极端情况,在使用上注意及时显示remove。
    • InheritableThreadLocal 也是JDK自带的实现,它主要解决了父子线程间的数据传递问题,但是在线程池中,由于线程是可复用的,InheritableThreadLocal 的继承特性不会自动生效。因此,实际上用的并不是很多。
    • TransmittableThreaLocal是阿里开源的一个用于解决线程池传递ThreadLocal数据问题的实现。它通过提前存储数据、然后通过 TtlRunnable 和 TtlCallable 对任务进行包装,在run的时候进行数据的回放设置到当前线程中,在线程执行结束后又通过restore恢复线程之前状态。实现了复用线程的ThreadLocal数据传递。
    • TransmittableThreaLocal是 Netty 框架中的一个类,它的主要特点是旨在提供比 JDK 自带的 ThreadLocal 更高的访问性能。它通过使用数组和固定索引的方式,而不是 JDK ThreadLocal 中的哈希表,从而减少了哈希冲突和提高了访问速度。通过字节填充避免伪共享。
转载自:https://juejin.cn/post/7362057695975440423
评论
请登录