ThreadLocal模式的实现机理

本文深入解析了Java中ThreadLocal的工作原理及其线程安全机制。通过分析ThreadLocal类的实现细节,解释了它是如何通过ThreadLocalMap为每个线程提供独立的变量副本,从而避免线程间的变量冲突。

在早期的JDK版本中,提供了一种解决多线程并发问题的方案:java.lang.ThreadLocal类。ThreadLocal类在维护变量时,实际使用了当前线程中的一个叫做ThreadLocalMap的独立副本,每个线程可以独立修改属于自己的副本而不会相互影响,从而隔离了线程和线程,避免了线程访问实例变量发生冲突的问题。

public class Thread implements Runnable{

    //这里省略了许多其他代码
    ThreadLocal.ThreadLocalMap threadLocals = null;

    @Override
    public void run() {

    }
}
这是JDK中Thread源码的一部分,从中我们可以看出ThreadLocalMap跟随当前的线程而存在。不同的线程Thread拥有不同的ThreadLocalMap的本地实例变量,这也就是“副本”的含义。接下来看看ThreadLocal.ThreadLocalMap是如何定义的,以及ThreadLocal如何来操作它

public class ThreadLocal<T> {

     //这里省略了许多代码

     //将value的值保存到当前线程的本地变量中
     public void set(T value){
        //获取当前线程
        Thread t = Thread.currentThread();
        //调用getmap方法获取当前线程中的本地变量ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        //如果ThreadLocalMap已经存在,直接使用
        if(map != null){
            //以当前的ThreadLocal的实力作为key,存储于当前线程的ThreadLocalMap中
            //如果当前线程中定义了多个不同的ThreadLocal的实力,则它们会作为不同的key进行存储互不干扰
            map.set(this, value);
        }else {
            //如果ThreadLocalMap不存在,则为当前线程创建一个新的
            creatMap(t, value);
        }
     }

      //获取当前线程中以当前ThreadLocal实例为key的变量值
      public T get(){
        //获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程中的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if(map != null){
            //获取当前线程中以当前ThreadLocal实例为key的变量值
            ThreadLocalMap.Entry e = map.getEntry(this);
            if(e != null)
                return (T)e.value;
        }
            //当map不存在时,设置初始值
            return 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;
       }
  //从当前线程中获取与之对应的ThreadLocalMap ThreadLocalMap getMap(Thread d){ return t.threadLocals; } //创建当前线程中的ThreadLocalMap void creatMap(Thread t, T firstValue){ //调用构造函数生成当前线程中的ThreadLocalMap t.threadLocals = new ThreadLocalMap(this, firstValue); } //ThreadLocalMap的定义 static class ThreadLocalMap{ //这里省略了许多代码 } }}
从上述代码中,我们看到了ThreadLocal类的大致结构和进行ThreadLocalMap的操作。我们可以得出以下结论:

1:ThreadLocalMap变量属于线程的内部属性,不同的线程拥有完全不同的ThreadLocalMap变量。

2:线程中的ThreadLocalMap变量的值实在ThreadLocal对象进行set或者get操作的时候创建的。

3:在创建ThreadLocalMap之前,会首先检查当前线程中的ThreadLocalMap变量是否已经存在,如果不存在则创建一个;如果存在,则使用当前线程已创建的ThreadLocalMap。

4:使用当前线程的ThreadLocalMap的关键在于使用当前ThreadLocal的实力作为key进行存储。

ThreadLocal模式至少从两个方面完成了数据访问隔离,即横向隔离和纵向隔离。有了横向和纵向两种不同的隔离方式,ThreadLocal模式就能真正地做到线程安全:

1:纵向隔离——线程与线程之间的数据访问隔离。这一点由线程的数据结构保证。因为每个线程在进行对象访问时,访问的都是各个线程自己的ThreadLocalMap;

2:横向隔离——同一线程中,不同的ThreadLocal实例操作对象之间相互隔离。这一点由ThreadLocalMap在存储时采用当前ThreadLocal的实例作为key来保证。


### ThreadLocal实现原理 #### 1. 线程局部变量的概念 ThreadLocal 是 Java 提供的一种机制,用于在多线程环境中为每个线程创建独立的变量副本。这使得不同线程之间不会相互干扰,从而解决了共享资源带来的同步问题[^1]。 #### 2. 内部结构:`ThreadLocalMap` 为了管理这些线程局部变量,ThreadLocal 使用了一个名为 `ThreadLocalMap` 的静态内部类来保存键值对。其中键是 ThreadLocal 对象本身,而值则是对应线程的具体数据实例。每当调用 `set()` 或者 `get()` 方法时,实际上都是在这个 map 中操作相应的 entry[^4]。 ```java class Thread { /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null; } ``` #### 3. 存储过程解析 当设置一个新的 ThreadLocal 变量时: - 如果当前线程还没有初始化过任何 ThreadLocal,则会先创建一个新的 `ThreadLocalMap`. - 接着会在该 Map 中插入一条记录,其 Key 就是指定的那个 ThreadLocal 实例, Value 则是要存入的数据。 ```java public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } ``` #### 4. 获取过程分析 读取某个特定于当前线程的 ThreadLocal 数据时: - 首先尝试从当前线程关联的 `ThreadLocalMap` 查找是否存在对应的 Entry. - 若存在则返回相应值;如果不存在或为空,则按照默认策略处理(通常抛出异常或返回null). ```java 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(); } ``` #### 5. 清理机制 为了避免因长时间持有无用的对象而导致内存泄露,在不再需要某些 ThreadLocal 变量的时候应当及时清理它们。可以通过显式调用 `remove()` 来移除指定项,或者依赖 JVM 自动回收弱引用指向的废弃条目[^2]. ```java protected void finalize() throws Throwable { try { remove(); // Remove this instance from current thread's map } finally { super.finalize(); } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值