ThreadLocal 源码分析

ThreadLocal 如何做到在多个线程对同一个共享变量进行 set 操作时,实现线程的完全隔离呢?

实际上,不少朋友已经猜到基本,从最终达到的效果来看,每个线程应该有一个与 ThreadLocal 关联的容器,它可以用来存储共享变量的初始化副本,当线程对该副本进行更新时,只更新存储在当前线程关联容器中的数据副本。然而实际情况是不是这样呢?

我们来了解一下,ThreadLocal 的整体设计原理,在每个线程中都会维护一个成员变量 ThreadLocalMap,其中 key 是一个指向 ThreadLocal 实例的弱引用、value 表示 ThreadLocal 的初始化值或者在当前线程中 set()方法的值


ThreadLocal 源码提供的方法不多,主要有:set(T value) 方法,get(方法),remove()方法和initialValue()方法

set() 方法源码分析

set()方法用于当前线程中设置一个值,并保存在该线程的 ThreadLocalMap 中,代码如下

public void set(T value) {
        Thread t = Thread.currentThread();//获取当前线程
        ThreadLocalMap map = getMap(t);//以当前线程为key,获取当前线程变量
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);//初始化线程threadLocals
    }
// 获取线程 t 的 ThreadLocalMap 成员
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
//  线程 t 创建一个ThreadLocalMap 成员  
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);//创建ThreadLocalMap对象(以当前ThreadLocal对象为key(firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1)),firstValue为value)初始化;赋值给当前线程
    }
  • 通过Thread.currentThread 得到当前线程
  • 调用getMap(t) 方法得到当前线程中的成员变量 ThreadLocalMap

以上源码可以看出set(T value)方法的执行流程,大致如下:

(1)获得当前线程,然后获得当前线程的 ThreadLocalMap 成员,暂存于map变量

(2)如果map不为空,就将Value设置到map中,当前的threadlocal作为key

(3)如果map为空,为该线程创建map,然后设置第一个“Key-Value对”,Key为当前的ThreadLocal实例,Value为set()方法的参数 value 值

get() 方法源码分析

get()方法用于获得“线程本地变量”在当前线程的 ThreadLocalMap 中对应的值,相当于获取线程本地值,其核心源码如下

private T get() {
    // 获取当前线程对象
    Thread t = Thread.currentThread();
    // 获得线程对象的 ThreadLocalMap 内部成员
    ThreadLocalMap map = getMap(t);

    // 如果当前线程的内部 map 成员存在
    if (map != null) {
        // 以当前 ThreadLocal 为 key ,尝试获得条目
        ThreadLocalMap.Entry e = map.getEntry(this);
        // 条目存在
        if(e != null){
            T result = (T)e.value;
            return result;
        }
    }
    // 如果当前线程对应的 map 不存在
    // 获取 map存在 ,但是当前 ThreadLocal 实例没有对应的 “Key-Value对”,返回初始值
    retrun setInitialValue();
}



// 设置 ThreadLocal 关联的初始值并返回
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;
}


通过以上源码可以看出T get() 方法的执行流程,大致如下:

(1)先尝试获得当前线程,然后获得当前线程的 ThreadLocalMap 成员,暂存于 map 变量

(2)如果获得的 map 不为空,那么以当前 ThreadLocal 实例为 Key 尝试获得 map 中的Entry(条目)

(3)如果 Entry 不为空,就返回 Entry 中的 value

(4)如果 Entry 为空,就通过调用 initialValue 初始化钩子函数获取 ThreadLocal 初始值,并设置在 Map 中。如果 map 不存在,还会给当前线程创建新 ThreadLocalMap 成员,并绑定第一个 "Key-Value对"

remove()方法

remove()方法用于当前线程的 ThreadLocalMap 中移除 “线程本地变量”所对应的值,其核心源码如下:

public void remove () {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if(m != null){
        m.remove(this);
    }
}

initialValue()方法

当“线程本地变量”在当前线程的ThreadLocalMap中尚未绑定值时,initialValue()方法用于获取初始值。其源码如下:

protected T initialValue() {
    return null;
}

如果没有调用 set() 直接调用 get(),就会调用该方法,但是该方法只会调用一次。默认情况下,initialValue()方法返回null ,如果不想返回null,可以继承 ThreadLocal 以覆盖此方法

真的需要继承 ThreadLocal 去重写 initialValue() 方法吗?其实没必要。JDK 已经为大家定义了一个了一个 ThreadLocal 的内部 SuppliedThreadLocal 静态子类,并且提供了 ThreadLocal.withInitial(...)静态工厂方法,方便大家在定义 ThreadLocal 实例时设置初始值回调函数,使用工厂方法构造 ThreadLocal 实例代码如下:

ThreadLocal<Foo> LOCAL_FOO = ThreadLocal.withInitial(() -> new Foo());

JDK 定义的 ThreadLocal.withInitial(...)静态工厂方法及其内部子类 SuppliedThreadLocal 的源码如下:

//ThreadLocal 工厂方法可以设置本地变量初始值钩子函数
public static <S> ThreadLocal<S> withInitial(
                        Supplier<? extends S> supplier) {
    return new SupplierThreadLocal<>(supplier);
}


// 内部静态子类
// 继承了 ThreadLocal ,重写了 initialValue()方法,返回钩子函数的值作为初始值
static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
    //保存钩子函数
    private final Supplier<? extends T> supplier;
    //传入钩子函数
    SuppliedThreadLocal(Supplier<? extends T> supplier) {
        this.supplier = Objects.requireNonNull(supplier);
    }

    @Override
    protected T initialValue(){
        return supplier.get(); //返回钩子函数的值作为初始值
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明雨星云

感谢,我会继续创作更优质作品

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值