JAVA线程本地存储ThreadLocal的源码分析

在jdk的api文档中,是这么描述ThreadLocal类的:This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. 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)。大概意思是说:ThreadLocal类是用于存储线程本地变量的,这些线程本地变量是通过ThreadLocal对象的get和set方法获取和存放的。ThreadLocald对象在一个类中一般是私有的静态的变量,存储那些和线程状态的变量,比如用户id或者事务id。api中还提供了一个简单的实例,在这里贴出来方便理解ThreadLocal的作用和使用。

 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
    //这里重写ThreadLocal的方法,在下面调用threadId.get(),由于没有初始化
    //会调用initalValue方法初始化value,并将value返回
     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();
     }
 }

在上面这个例子中就是通过ThreadLocal对象threadId设置一个id作为线程id,然后通过get方法获取线程id。这样,每个线程都会有唯一一个ID,即每个线程持有线程本地变量。那么在ThreadLocal类内部中是怎样存储和获取一个线程本地变量的呢。下面通过源码分析ThreadLocal的工作原理。

首先,来看Thread这个类

Thread定义了一个ThreadLocal.ThreadLocalMap的成员变量threadLocals。注意了,这个threadLocals对象是存储线程本地变量的载体,即一个线程的所有ThreadLocal对象的变量存储在threadLocals这个对象中,其中ThreadLocal对象为key,线程本地变量为value。

下面来看看ThreadLocal的set方法

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

getMap()方法的源码如下:

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

set方法中先获取当前线程对象,然后获Thread线程对象中的threadLocals变量,也就是在上文提到的存储线程本地变量的载体。如果这个threadLocals对象是非null,就以ThreadLocal对象引用为key,保存线程本地变量。以文章开头的例子来说,就是将threadId对象引用作为key,将nextId.getAndIncrement()的值作为value存储。

再看看ThreadLocal的get方法

    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();
    }

get方法也是先获取当前线程对象,在获取线程对象中的threadLocals变量。若threadlocals非null,以当前ThreadLocal对象引用为key,获取变量。若为null,执行setInitialValue()方法。来看看setInitialValue()方法源码。

private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

在该方法中会调用initialValue()方法,这个方法默认返回null,所以如果不重写这个方法,又不调用set()方法设置值,调用get()方法就会返回null。文章开头的例子重写了该方法。

总结:ThreadLocal工作的原理就是通过以自身引用为key,将待要保存的线程变量保存在Thread对象的成员变量threadLocals中。

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThreadLocal类是Java多线程编程中非常有用的一个类,它提供了一种线程本地变量的机制。线程本地变量是指每个线程都有自己独立的变量副本,互不干扰,可以避免线程间的数据共享问题。下面是ThreadLocal类的用法及一些深入的解释。 1. 基本用法: ThreadLocal类的使用非常简单,可以通过以下几个步骤实现: - 创建ThreadLocal对象:`ThreadLocal<T> threadLocal = new ThreadLocal<>()` - 设置线程本地变量:`threadLocal.set(value)` - 获取线程本地变量:`T value = threadLocal.get()` - 清除线程本地变量:`threadLocal.remove()` 2. 实际应用: ThreadLocal类在多线程编程中有广泛的应用,特别是在以下场景中: - 数据库连接管理:每个线程都可以拥有自己的数据库连接,避免了线程间共享连接的问题。 - 事务管理:每个线程可以独立管理自己的事务,避免了事务数据的混乱。 - 用户身份信息传递:在Web应用中,可以将用户身份信息存储到ThreadLocal中,方便不同组件访问。 - 线程上下文信息传递:可以将一些线程上下文信息存储到ThreadLocal中,方便不同线程间的信息传递。 3. 实现原理ThreadLocal类的实现原理比较复杂,它通过一个ThreadLocalMap来维护每个线程变量副本。在每个ThreadLocal对象中都有一个ThreadLocalMap实例,用于存储线程本地变量的值。当调用ThreadLocal的set方法时,实际上是将值存储到当前线程ThreadLocalMap中;当调用get方法时,实际上是从当前线程ThreadLocalMap中获取值。 4. 注意事项: - 内存泄漏:由于ThreadLocalMap中的Entry对象使用ThreadLocal的弱引用作为键,如果ThreadLocal没有被及时清理,可能会导致内存泄漏问题。因此,在使用完ThreadLocal后,应该调用remove方法进行清理。 - 初始化值:通过重写ThreadLocal的initialValue方法,可以为每个线程变量副本提供一个初始值。 总结: ThreadLocal类提供了一种简单而有效的方式来实现线程本地变量。它在多线程编程中有广泛的应用,可以避免线程间数据共享的问题。但需要注意内存泄漏和初始值的问题。希望以上解释对你有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值