浅析线程本地存储--ThreadLocal

2019, Aug 09    

一、为什么会有线程本地存储?

在实现线程安全时,有一种策略是无同步方案。

如果一段代码中所需要的数据必须与其他代码共享,那就看看这些共享数据的代码是否能保证在同一个线程中执行。如果能保证,我们就可以把共享数据的可见范围限制在同一个线程之内,这样,无须同步也能保证线程之间不出现数据争用的问题。

由此就有了线程本地存储的使用场景。

二、ThreadLocal 使用

public class Test {
    public static ThreadLocal<Integer> tl = new ThreadLocal<>();
    public static CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
    public static void main(String[] args) {
        Thread t1 = new MyThread(1);
        Thread t2 = new MyThread(2);
        t1.start();
        t2.start();
    }
    static class MyThread extends Thread{
        Integer value;
        public MyThread(Integer value){
           this.value =  value;
        }
        @Override
        public void run(){
            setValue();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            printValue();

        }
        private void setValue(){
            System.out.println("setValue......");
            tl.set(value);
            list.add(0,value);
        }
        private void printValue(){
            System.out.println("ThreadLocal "+Thread.currentThread()+" "+tl.get());
            System.out.println("List "+Thread.currentThread()+" "+list.get(0));
        }
    }
}

输出为

setValue......
setValue......
ThreadLocal Thread[Thread-1,5,main] 2
ThreadLocal Thread[Thread-0,5,main] 1
List Thread[Thread-0,5,main] 2
List Thread[Thread-1,5,main] 2

通过与线程共享的List对比,可以看到,ThreadLocal中持有的对象是基于每线程的。

三、分析源码

ThreadLocal中的set方法:

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

从getMap()点进去:

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

可以看到,每个线程有一个threadLocalMap。看代码好像不是很清晰,看下图就很好懂了

同理,对于get方法,也是先获取当前线程的ThreadLocalMap,然后以调用get的当前ThreadLocal为key,获取到持有的Object。

 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();
}

总结

每个线程有一个ThreadLocalMap,新建一个ThreadLocal并set一个value时,会把这个ThreaLocal作为key,value作为值存到当前线程的ThreadLocalMap中。

简单的分析,如有错漏之处,希望海涵并不吝赐教!

参考文献

  1. 深入JDK源码之ThreadLocal类