目录
- 介绍
- 1.1ThreadLocal
- 1.2使用场景
- 2.使用
- 2.1引入依赖
- 2.2 创建 TransmittableThreadLocal 实例:
- 2.3 设置和获取值
- 2.4 线程切换时传递值:
- 3.原理
介绍
TransmittableThreadLocal 是 Alibaba 开源的一个库,旨在解决在多线程环境中正确处理 ThreadLocal的问题。它提供了一个能够在线程间传递 ThreadLocal 值的特性。
1.1ThreadLocal
ThreadLocal 是 Java 中的一个线程级别的变量,它提供了一种线程私有的变量存储机制。每个线程都有自己独立的 ThreadLocal 实例,可以用于在线程内部存储和获取数据,而不会与其他线程共享。
- 线程隔离的变量存储:
ThreadLocal 提供了一种在线程内部存储变量的机制。通过 ThreadLocal,每个线程都可以拥有自己独立的变量副本,这些变量副本在其他线程中不可见,实现了线程之间的数据隔离。 - 数据存取操作:
ThreadLocal 提供了一些方法来存储和获取变量的值,包括:
set(T value):将变量的值设置为指定值。
get():获取当前线程的变量值。
remove():从当前线程中删除变量。 - 线程切换的变量连续性:
当线程切换时,ThreadLocal 可以保持变量的连续性。也就是说,新线程可以继续访问之前线程存储的变量值。这在一些场景下非常有用,例如使用线程池时,任务在不同的线程中执行,但仍然需要访问之前线程中存储的上下文信息。 - 避免线程间的共享和竞争条件:
由于每个线程拥有自己的 ThreadLocal 实例,因此变量的存取不会引起线程之间的竞争条件。每个线程都可以独立地存取自己的变量副本,不需要使用同步机制来保护变量的访问。
需要注意的是,尽管 ThreadLocal 提供了一种在线程内部存储变量的机制,但它并不适合在所有情况下使用。过度使用 ThreadLocal 可能导致内存泄漏或引起上下文信息的错误传递。在使用 ThreadLocal 时,需要仔细评估和测试代码,确保正确使用和管理变量的生命周期。
在 Java 中,ThreadLocal 是作为一个类提供的,位于 包中。可以使用 ThreadLocal 类的实例来创建线程级别的变量,并使用其提供的方法进行操作。
1.2使用场景
在传统的多线程环境中,ThreadLocal 变量是线程私有的,即每个线程都有自己独立的 ThreadLocal 值。然而,在一些特定场景下,我们希望在线程切换时能够传递 ThreadLocal 值,以保持特定线程的上下文信息的连续性。这就是 TransmittableThreadLocal 的用武之地。
2.使用
2.1引入依赖
<dependency>
<groupId></groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.13.0</version>
</dependency>
2.2 创建 TransmittableThreadLocal 实例:
private static ThreadLocal<String> myThreadLocal = new TransmittableThreadLocal<>();
//TransmittableThreadLocal<>支持传入不同数据结构
private static final TransmittableThreadLocal<Map<String, Object>> inheritableThreadLocal = new TransmittableThreadLocal<>();
2.3 设置和获取值
//String
myThreadLocal.set("Hello, World!");
String value = myThreadLocal.get();
//Map
Map<String, Object> threadMap = new HashMap<>();
threadMap.put("userinfo", ssoUserInfo);
threadMap.put("referer", referer);
inheritableThreadLocal.set(threadMap);
2.4 线程切换时传递值:
当线程切换时,TransmittableThreadLocal
可以自动将当前线程的 ThreadLocal
值传递给新的线程。这样,新线程可以继续使用之前线程的上下文信息。例如,使用线程池时,当任务从一个线程切换到另一个线程时,TransmittableThreadLocal
可以保持 ThreadLocal
值的连续性。
需要注意的是,TransmittableThreadLocal
并非是 Java 标准库中的类,它是 Alibaba 开源的一个扩展库,为了解决 ThreadLocal
在跨线程传递值时的问题而设计。在使用 TransmittableThreadLocal
时,需要确保代码中正确引入了该库,并替换相应的 ThreadLocal
实例。
使用 TransmittableThreadLocal
需要谨慎,因为它在某些情况下可能导致内存泄漏或上下文信息的错误传递。需要仔细评估和测试代码,确保正确处理 ThreadLocal
值的传递,并避免潜在的问题。
3.原理
-
初始线程设置值:
在初始线程中,当调用
TransmittableThreadLocal
的set()
方法设置ThreadLocal
的值时,实际上会将值存储在一个全局的映射表中,以线程 ID 为键,值为ThreadLocal
的值。@Override public final void set(T value) { super.set(value); // may set null to remove value if (null == value) removeValue(); else addValue(); } @Override public final T get() { T value = super.get(); if (null != value) addValue(); return value; } private void addValue() { if (!holder.get().containsKey(this)) { holder.get().put(this, null); // WeakHashMap supports null value. } } //使用 InheritableThreadLocal 类创建了一个实例 holder。InheritableThreadLocal 是 ThreadLocal 的子类,它允许在父线程和子线程之间传递值。 private static InheritableThreadLocal<Map<TransmittableThreadLocal<?>, ?>> holder = new InheritableThreadLocal<Map<TransmittableThreadLocal<?>, ?>>() { //通过重写 initialValue() 方法,定义了在初始线程中创建的映射表对象。这个映射表使用 WeakHashMap 来存储 TransmittableThreadLocal 实例和对应的值。WeakHashMap 的使用可以避免造成内存泄漏,因为它使用弱引用来保存键,当键不再被其他引用持有时,将会被垃圾回收。 @Override protected Map<TransmittableThreadLocal<?>, ?> initialValue() { return new WeakHashMap<TransmittableThreadLocal<?>, Object>(); } //通过重写 childValue() 方法,定义了在子线程中创建的映射表对象。这个方法接收父线程中的映射表对象作为参数,并通过构造一个新的 WeakHashMap 对象来创建子线程的映射表。由于 WeakHashMap 是基于弱引用的,它会继承父线程的映射表并在需要时进行垃圾回收。 @Override protected Map<TransmittableThreadLocal<?>, ?> childValue(Map<TransmittableThreadLocal<?>, ?> parentValue) { return new WeakHashMap<TransmittableThreadLocal<?>, Object>(parentValue); } };
-
线程切换时传递值:
当线程发生切换时,例如使用线程池或线程复用机制时,新的线程被分配或复用,原始线程的
ThreadLocal
值将被传递给新的线程。 -
新线程获取值:
在新的线程中,当调用
TransmittableThreadLocal
的get()
方法获取ThreadLocal
的值时,它首先会获取当前线程的线程 ID,并通过全局映射表查找原始线程的ThreadLocal
值。 -
内存清理和释放:
TransmittableThreadLocal
会根据线程的生命周期进行相应的内存清理和释放。当线程完成后,相关的ThreadLocal
值将被清理,并且不会影响其他线程。