ThreadLocal原理

文章通过示例代码展示了ThreadLocal在不同线程间如何保持数据独立性,然后从源码层面解释了ThreadLocal的get()、set()和remove()方法的工作原理,以及ThreadLocalMap的内部实现,强调了每个线程都有自己的ThreadLocalMap存储变量,确保线程间的隔离性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

public class ThreadLocalTest {
private static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
public static void main(String[] args){
//创建第一个线程
Thread threadA = new Thread(()->{
threadLocal.set("ThreadA:" + Thread.currentThread().getName());
System.out.println("线程A本地变量中的值为:" + threadLocal.get());
});
//创建第二个线程
Thread threadB = new Thread(()->{
threadLocal.set("ThreadB:" + Thread.currentThread().getName());
System.out.println("线程B本地变量中的值为:" + threadLocal.get());
});
//启动线程A和线程B
threadA.start();
threadB.start();
}
}

上面这段代码执行结果为

线程A本地变量中的值为:ThreadA:Thread-0

线程B本地变量中的值为:ThreadB:Thread-1

public class ThreadLocalTest {
private static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
public static void main(String[] args){
//创建第一个线程
Thread threadA = new Thread(()->{
threadLocal.set("ThreadA:" + Thread.currentThread().getName());
System.out.println("线程A本地变量中的值为:" + threadLocal.get());
threadLocal.remove();
System.out.println("线程A删除本地变量后ThreadLocal中的值为:" + threadLocal.get());
});
//创建第二个线程
Thread threadB = new Thread(()->{
threadLocal.set("ThreadB:" + Thread.currentThread().getName());
System.out.println("线程B本地变量中的值为:" + threadLocal.get());
System.out.println("线程B没有删除本地变量:" + threadLocal.get());
});
//启动线程A和线程B
threadA.start();
threadB.start();
}
}

上面这段代码执行结果为

线程A本地变量中的值为:ThreadA:Thread-0

线程B本地变量中的值为:ThreadB:Thread-1

线程B没有删除本地变量:ThreadB:Thread-1

线程A删除本地变量后ThreadLocal中的值为:null

由上面的代码可以看出AB线程中对ThreadLocal的操作是互不影响的.

下面我们就从源码的角度来剖析一下

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocal.ThreadLocalMap map = this.getMap(t);
        if (map != null) {
            ThreadLocal.ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                T result = e.value;
                return result;
            }
        }

        return this.setInitialValue();
    }

从以上的返回值来看,get()方法最后会返回种结果

1.从Thread.currentThread()中取的结果

2.从this.setInitialValue()返回的结果

接下来只需要看一下这里两个地方是取得值是啥玩意

首先来看getMap()方法

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

从此可以看出get方法是返回的当前线程Thead的threadLocals变量

接下来我们来看一下this.setInitialValue()中的逻辑

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

        if (this instanceof TerminatingThreadLocal) {
            TerminatingThreadLocal.register((TerminatingThreadLocal)this);
        }

        return value;
    }

该代码中主要的逻辑为将的this.initialValue()中的值设置到当前线程中的Map中,也就是threadLocals中,并且返回value。如果threadLocals为空,则新建一个ThreadLocalMap存入当前线程的threadLocals变量中。

接下来我们来看一下initialValue中的逻辑,可以看到返回的是null,子类也可以覆写该方法

protected T initialValue() {
        return null;
    }

createMap()方法,这里划重点,要考
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocal.ThreadLocalMap(this, firstValue);
    }

        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            this.table = new ThreadLocal.ThreadLocalMap.Entry[16];
            int i = firstKey.threadLocalHashCode & 15;
            this.table[i] = new ThreadLocal.ThreadLocalMap.Entry(firstKey, firstValue);
            this.size = 1;
            this.setThreshold(16);
        }
ThreadLocalMap就是ThreadLocal的精髓所在

从ThreadLocalMap的构造函数我们可以看到,每个存入ThreadLocal中的值都被存入的table中,而table中则是Entry,Entry的key是ThreadLocal对象hash之后的值,value则是需要隔离的对象。

到这里我们就可以看出ThreadLocal储存的原理了

当我们向线程中取出value值时,实际上就是从当前线程的ThreadLocalMap中取值,而每个线程都会持有一个ThreadLocalMap(ThreadLocalMap的key为ThreaLocal做hash之后的值,value为需要存入线程中的变量),所以每个线程对ThreadLocalMap的操作是互相不影响的

现在我们来看一下set方法

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

    }

通过我们前面对get方法的解读,现在看set方法就容易很多了。

将当前线程的threadLocals取出然后往里面set值,如果map为空,则新建

最后我们看一下remove方法

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

    }

就是将当前线程的threadLocals取出然后干掉,具体怎么干掉的,就参考ThreadLocalMap的构造函数,反向操作,自己脑补一下吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值