ThreadLocal是如何实现线程隔离的?

多个线程修改ThreadLocal里面的值,会是什么情况?

我们写一个测试代码:

public class ThreadLocalTest {
    
    

    public static void main(String[] args) {
    
    
        ThreadLocal<String> sharedName = new ThreadLocal<>();
        ThreadLocal<Integer> sharedId = new ThreadLocal<>();

        //main线程最先修改
        System.out.println(Thread.currentThread().getName() + "初始值:" + sharedName.get()+" "+sharedId.get());
        sharedName.set("main");
        sharedId.set(103);
        System.out.println(Thread.currentThread().getName() + "设定后:" + sharedName.get()+" "+sharedId.get()+"\n");


        Thread tA = new Thread() {
    
    

            @Override
            public void run() {
    
    
                System.out.println(this.getName() + "开始值:" + sharedName.get()+" "+sharedId.get());
                sharedName.set("线程A");
                sharedId.set(101);
                sleepTime(100);
                System.out.println(this.getName() + "设定后:" + sharedName.get()+" "+sharedId.get()+"\n");
            }

        };
        Thread tB = new Thread() {
    
    
            @Override
            public void run() {
    
    
                sleepTime(100);
                System.out.println(this.getName() + "开始值:" + sharedName.get()+" "+sharedId.get());
                sharedName.set("线程B");
                sharedId.set(102);
                System.out.println(this.getName() + "设定后:" + sharedName.get()+" "+sharedId.get()+"\n");
            }
        };
        tA.start();
        tB.start();
    }


    private static void sleepTime(long millis) {
    
    
        try {
    
    
            Thread.sleep(millis);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }

}

输出结果:

main初始值:null null
main设定后:main 103

Thread-0开始值:null null
Thread-0设定后:线程A 101

Thread-1开始值:null null
Thread-1设定后:线程B 102


程序有三个线程main,tA,tB,三个线程都修改了sharedName和sharedId(后面简称name和id),但是main线程设置的name和id,tA和tB并没有读取到,ta和tB一开始读取的是null。

为什么tA线程读不到main线程设置的name?

看ThreadLoca.set()方法:

//ThreadLocal.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);
        }
    }

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

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

//ThreadLocalMap是ThreadLocal的静态内部类
//ThreadLocalMap的构造器
        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    
    
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

可以看到,set方法,通过getMap()获取当前线程的ThreadLocalMap, 然后把设置的字符串“main”,以<key,value>的形式存放在ThreadLocalMap里面。看下面Thread的源码可知,每个Thread都有一个ThreadLocalMap类型的成员变量。

public
class Thread implements Runnable {
    
    
	...
    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ...
}

所以,保存的时候,保存在当前线程的ThreadLocalMap里面。那读取的时候又是怎么读的?
看看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();
    }

    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);
        }
        if (this instanceof TerminatingThreadLocal) {
    
    
            TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
        }
        return value;
    }
    
    protected T initialValue() {
    
    
        return null;
    }

可以看出,get()方法在读取的时候,也是读的当前线程的ThreadLocalMap。所以tA线程读取的name是tA线程自己的ThreadLocalMap,一开始map为空,所以name是null,后来设置值了,就读到了自己设置的值。

明明是同一个ThreadLocal对象,为啥获取的是不同name值了?
这就好比,两个不同的HashMap,mapA 和mapB , 它俩都有一组key值相同的Entry,但是value值不一样,所以读取的value也不一样。下图一目了然,tl是ThreadLocal的缩写。

结论:tA线程读取name,是由ThreadLocal控制的,ThreadLocal获取的是tA线程自己的ThreadLocalMap,所以tA线程读取的也是自己的ThreadLocalMap的name值。读不到main线程设置的name值。

猜你喜欢

转载自blog.csdn.net/zhangjin1120/article/details/131466530
今日推荐