ThreadLocal,java.lang包中的一个类,它提供了线程局部变量。这些变量不同于它们的正常变量,因为每一个访问这个变量的线程都有其自己独立初始化的变量副本。ThreadLocal
实例通常用作保存线程上下文信息,如用户 ID、事务 ID 等。
package java.lang;
import java.lang.ref.*;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
/**
* This class provides thread-local variables. These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* {@code get} or {@code set} method) has its own, independently initialized
* copy of the variable. {@code ThreadLocal} instances are typically private
* static fields in classes that wish to associate state with a thread (e.g.,
* a user ID or Transaction ID).
*
* <p>For example, the class below generates unique identifiers local to each
* thread.
* A thread's id is assigned the first time it invokes {@code ThreadId.get()}
* and remains unchanged on subsequent calls.
* <pre>
* import java.util.concurrent.atomic.AtomicInteger;
*
* public class ThreadId {
* // Atomic integer containing the next thread ID to be assigned
* private static final AtomicInteger nextId = new AtomicInteger(0);
*
* // Thread local variable containing each thread's ID
* private static final ThreadLocal<Integer> threadId =
* new ThreadLocal<Integer>() {
* @Override protected Integer initialValue() {
* return nextId.getAndIncrement();
* }
* };
*
* // Returns the current thread's unique ID, assigning it if necessary
* public static int get() {
* return threadId.get();
* }
* }
* </pre>
* <p>Each thread holds an implicit reference to its copy of a thread-local
* variable as long as the thread is alive and the {@code ThreadLocal}
* instance is accessible; after a thread goes away, all of its copies of
* thread-local instances are subject to garbage collection (unless other
* references to these copies exist).
*
* @author Josh Bloch and Doug Lea
* @since 1.2
*/
public class ThreadLocal<T>
以下是关于 ThreadLocal
的详细解释:
1. 基本概念
ThreadLocal
提供了线程本地变量。这些变量不同于它们的正常变量,因为每一个访问这个变量的线程都有其自己独立初始化的变量副本。ThreadLocal
变量通常被 private static 修饰。这是一个多线程环境下变量访问安全的一个基石。
2.实现原理
ThreadLocal
的实现原理主要依赖于以下几个方面:
- ThreadLocalMap:
ThreadLocal
类内部维护了一个ThreadLocalMap
,这个Map
的键是ThreadLocal
对象,值则是线程本地变量的副本。每个线程都持有一个ThreadLocalMap
的引用,这个引用被封装在Thread
类的一个名为threadLocals
的成员变量中。当线程首次调用某个ThreadLocal
对象的get()
或set(T value)
方法时,就会在其threadLocals
中创建对应的ThreadLocalMap
。 - 哈希表:
ThreadLocalMap
的实现是一个简单的哈希表,其中ThreadLocal
对象作为键,线程本地变量的值作为值。这个哈希表允许快速访问和修改线程本地变量的值。 - 弱引用:在
ThreadLocalMap
中,键(即ThreadLocal
对象)被存储为弱引用(WeakReference
)。这意味着当ThreadLocal
对象不再被其他地方引用时,它可以被垃圾收集器回收,即使它仍然在ThreadLocalMap
中作为键存在。这有助于防止内存泄漏,但程序员仍需要在使用完ThreadLocal
后显式调用remove()
方法来从ThreadLocalMap
中删除对应的条目,以确保完全释放内存。 - 线程隔离:由于每个线程都有自己的
ThreadLocalMap
,因此每个线程都可以独立地访问和修改其线程本地变量的值,而不会影响到其他线程。这种线程隔离的特性使得ThreadLocal
在多线程编程中非常有用。
总的来说,ThreadLocal
的实现原理是通过在每个线程内部维护一个独立的 ThreadLocalMap
来实现线程本地变量的存储和访问。这种机制使得每个线程都可以拥有自己独立的变量副本,从而避免了多线程之间的数据共享和同步问题。
3. 使用场景
- 当你想让某个变量与线程绑定,而不是与类的实例绑定时,可以使用
ThreadLocal
。 - 当你不想在方法参数或类字段中传递一个变量时,
ThreadLocal
可以作为替代方案。
4. 主要方法
get()
: 返回此线程局部变量的当前线程副本的值。set(T value)
: 设置此线程局部变量的当前线程副本的值。remove()
: 移除此线程局部变量的值。如果此线程局部变量随后被当前线程读取(通过其get()
方法),或者通过其set()
方法尝试重新设置其值,它将重新调用其initialValue()
方法。initialValue()
: 返回此线程局部变量的当前线程的“初始值”。
5. 示例
public class ThreadLocalExample {
// 创建一个 ThreadLocal 变量
private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
// 线程1
Thread thread1 = new Thread(() -> {
threadLocal.set("Thread1's Value");
System.out.println(Thread.currentThread().getName() + " : " + threadLocal.get());
});
// 线程2
Thread thread2 = new Thread(() -> {
threadLocal.set("Thread2's Value");
System.out.println(Thread.currentThread().getName() + " : " + threadLocal.get());
});
thread1.start();
thread2.start();
}
}
输出:
Thread-0 : Thread1's Value
Thread-1 : Thread2's Value
6. 注意事项
ThreadLocal
的使用需要谨慎,因为它可能会导致内存泄漏,特别是在使用线程池时。这是因为线程池中的线程是复用的,而ThreadLocal
变量可能会在这些线程中被持续地设置,但如果没有被显式地移除,那么这些变量就会一直存在,直到线程结束。因此,当使用完ThreadLocal
变量后,最好调用remove()
方法来清理。ThreadLocal
不适用于跨线程传递数据,因为每个线程只能访问其自己的副本。
7. 替代方案
InheritableThreadLocal
:与ThreadLocal
类似,但它允许子线程访问父线程的ThreadLocal
变量。Transmission
或Context Propagation
库:这些库提供了更高级的功能,如跨线程和跨组件的数据传递。
总之,ThreadLocal
是 Java 中一个强大的工具,用于在多线程环境中管理线程特定的数据。但使用时需要谨慎,以避免潜在的问题,如内存泄漏。