当前位置:   article > 正文

ThreadLocal是如何实现线程隔离的?_thread local 需要设置什么属性才能使得线程隔离

thread local 需要设置什么属性才能使得线程隔离

多个线程修改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();
        }
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

输出结果:

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

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

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


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

程序有三个线程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);
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

可以看到,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;
    ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

所以,保存的时候,保存在当前线程的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;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

可以看出,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值。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/294516
推荐阅读
相关标签
  

闽ICP备14008679号