ThreadLocal原理解析

本文详细解析了ThreadLocal与InheritableThreadLocal的工作原理,重点阐述了两者在多线程环境下如何存储和传递数据,通过示例展示了InheritableThreadLocal如何在父子线程间复制数据。

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

涉及到三个实体类

  1. ThreadLocal
  2. Thread
  3. ThreadLocalMap
获取到了ThreadLocalMap
Thread+ThreadLocal=变量
变量
Thread
ThreadLocal
//ThreadLocalMap是ThreadLocal的内部类
ThreadLocalMap{
	//类似于HashMap,键值分别是ThreadLocal、对应的值
	Entry<ThreadLocal, Object>[] entry;
}

Thread{
	//保存了线程私有的ThreadLocalMap
	ThreadLocalMap map;
}

ThreadLocal{
	//获取被哪个线程调用的
	Thread t = Thread.currentThread();
	//获取当前线程在该ThreadLocal下的
	get(){
		//获取该线程的ThreadLocalMap
		map_temp = t.map;
		//如果map_temp为空,则根据自己重写的初始化策略进行初始化,并存入对应的ThreadLocalMap中对应的槽中
		if(map_temp == null){
			initVal = init();
			map_temp.set(this, initVal);
		}
		//类似于HashMap根据键查找值,这里的键是ThreadLocal对象本身
		return map_temp.get(this);
	}
}

可继承的InheritableThreadLocal

public class Test {

    private static final ThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();

    public static void main(String[] args) {
		//父线程
        new Thread(()->{
            inheritableThreadLocal.set("hzw");
			//子线程
            new Thread(()->{
                System.out.println(inheritableThreadLocal.get());
            }).start();

        }).start();
    }
}

运行结果是打印出hzw
准确来说是复制父线程的数据,当父线程remove掉后,子线程还可以获取到,示例如下

public class Test {

    private static final ThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();

    public static void main(String[] args) {

        new Thread(()->{
            inheritableThreadLocal.set("hzw");

            new Thread(()->{
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(inheritableThreadLocal.get());
            }).start();

            inheritableThreadLocal.remove();
            System.out.println("属性移除");
        }).start();
    }
}

运行结果是hzw,说明父线程移除后,子线程仍保留

源码解析:4个主要阶段

public class Test {

    private static final ThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();

    public static void main(String[] args) {
		//父线程
        new Thread(()->{   ..........................................1
            inheritableThreadLocal.set("hzw");   ....................2
			//子线程
            new Thread(()->{   ......................................3
                System.out.println(inheritableThreadLocal.get());   .4
            }).start();

        }).start();
    }
}
1.创建父线程(第1行对应的代码)
 if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

上述代码是Thread初始化时执行的,由于父线程的父线程是main,main中还没进行ThreadLocal操作,所以parent.inheritableThreadLocals==null,上述if逻辑内代码不执行

2.父线程存储值(第2行对应的代码)

InheritableThreadLocal没有重写set方法,所以set方法在ThreadLocal中调用,紧接着set方法中调用了createMap方法,此方法被重写,所以执行以下代码:

    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }

父子类createMap的唯一区别:ThreadLocal中创建的Map存储在t.threadLocals中,而InheritableThreadLocal中的createMap方法存储在t.inheritableThreadLocals中。

3.子线程初始化(第3行对应的代码)
 if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

第二步后,父线程的inheritableThreadLocals不为空,上述代码的if逻辑会执行,传入父线程的inheritedMap,为当前线程即子线程创建InheritedMap,并赋值给inheritableThreadLocals。具体创建过程如下:

		private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

其实就是将父线程的map复制给子线程,所以父线程map移除后,子线程会保留

4.子线程取值(第4行对应的代码)
 public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);   .......................1
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

调用的是ThreadLocal中的get,我们主要关心ThreadLocalMap map = getMap(t);这一行。这个和createMap一样被重写了,子类和父类实现逻辑是分别返回threadLocals和interitableThreadLocals,相当于set和get方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值