聊一聊java中的ThreadLocal

本文详细介绍了Java的ThreadLocal类,它如何提供线程局部变量,每个线程有独立副本,常用于存储线程上下文信息。文章探讨了其实现原理、使用场景、主要方法以及注意事项,以及与InheritableThreadLocal和Transmission/ContextPropagation库的比较。
摘要由CSDN通过智能技术生成

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&lt;Integer&gt; threadId =
 *         new ThreadLocal&lt;Integer&gt;() {
 *             &#64;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 的实现原理主要依赖于以下几个方面:

  1. ThreadLocalMapThreadLocal 类内部维护了一个 ThreadLocalMap,这个 Map 的键是 ThreadLocal 对象,值则是线程本地变量的副本。每个线程都持有一个 ThreadLocalMap 的引用,这个引用被封装在 Thread 类的一个名为 threadLocals 的成员变量中。当线程首次调用某个 ThreadLocal 对象的 get() 或 set(T value) 方法时,就会在其 threadLocals 中创建对应的 ThreadLocalMap
  2. 哈希表ThreadLocalMap 的实现是一个简单的哈希表,其中 ThreadLocal 对象作为键,线程本地变量的值作为值。这个哈希表允许快速访问和修改线程本地变量的值。
  3. 弱引用:在 ThreadLocalMap 中,键(即 ThreadLocal 对象)被存储为弱引用(WeakReference)。这意味着当 ThreadLocal 对象不再被其他地方引用时,它可以被垃圾收集器回收,即使它仍然在 ThreadLocalMap 中作为键存在。这有助于防止内存泄漏,但程序员仍需要在使用完 ThreadLocal 后显式调用 remove() 方法来从 ThreadLocalMap 中删除对应的条目,以确保完全释放内存。
  4. 线程隔离:由于每个线程都有自己的 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 中一个强大的工具,用于在多线程环境中管理线程特定的数据。但使用时需要谨慎,以避免潜在的问题,如内存泄漏。

  • 16
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值