ThreadLocal学习

ThreadLocal是一个线程内部的数据存储类,通过它可以在同一个线程内共享数据。

ThreadLocal原理

ThreadLocal学习_第1张图片
ThreadLocal

代码分析

从demo说起

/*
* 持有ThreadLocal的类
**/
public class ThreadLocalContext {

    private static final ThreadLocal NAME_LOCAL = new ThreadLocal<>();
    private static final ThreadLocal AGE_LOCAL = new ThreadLocal<>();

    public static void setName(String name) {
        NAME_LOCAL.set(name);
    }

    public static void setAge(Integer age) {
        AGE_LOCAL.set(age);
    }

    public static String getName() {
        return NAME_LOCAL.get();
    }

    public static Integer getAge() {
        return AGE_LOCAL.get();
    }
}
//测试代码
public static void main(String[] args) {
    new Thread(()->{
        ThreadLocalContext.setAge(1);
        ThreadLocalContext.setName("test");
        print();
    }).start();

    new Thread(()->{
        ThreadLocalContext.setAge(10);
        ThreadLocalContext.setName("xixi");
        print();
    }).start();

}

private static void print(){
    String desc=ThreadLocalContext.getName()+"-"+ThreadLocalContext.getAge();
    System.out.println(Thread.currentThread().getName()+":"+desc);
}

根据上面的demo代码,我们就从get/set进行分析ThreadLocal的代码

从上面代码中可以看到,ThreadLocalContext持有了两个ThreadLocal对象NAME_LOCALAGE_LOCAL

ThreadLocal.set()

我们首先从setName和setAge说起。

public class ThreadLocal{
    //set 方法
    public void set(T value) {
        Thread t = Thread.currentThread();//1.获取当前线程
        ThreadLocalMap map = getMap(t);//2.从当前线程中获取到ThreadLocalMap
        if (map != null)
            map.set(this, value); //3.如果不为空,将当前的ThreadLocal对象为key,存储到当前线程的ThreadLocalMap中
        else
            createMap(t, value); //4.如果为空,需要为当前Thread创建ThreadLocalMap对象
    }
}

可以看到上面的代码,为什么可以隔离每个线程的数据。

就是因为ThreadLocalMap是Thread的成员变量,所以数据是存储到Thread对象里的,别的Thread是没办法获取到其他线程的对象。

ThradLocal.get()
public class ThreadLocal{
    public T get() {
        Thread t = Thread.currentThread(); //1.获取当前线程
        ThreadLocalMap map = getMap(t); //2.通过当前线程获取ThreadLocalMap
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this); //3.根据当前的ThreadLocal为key,获取Entry对象
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();//4. 初始化ThreadLocalMap
    }
    
    static class ThreadLocalMap{
        //从上面步骤3过来
        private Entry getEntry(ThreadLocal key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }
    }
    
}

ThreadLocalMap是使用一个Entry[]存储ThreadLocal对象,每个ThreadLocal都有一个hashCode,每创建一个ThreadLocal其对应的hashCode会加上0x61c88647,以保证散列足够的散。

即使这样,仍然会出现hash冲突的情况,所以在getEntry的方法中,存在获取到entry会当前ThreadLocal不一致的情况,此时会继续执行getEntryAfterMiss方法。

如果再去分析源码的话,会看到Entry实际上是继承了WeakReference,来标识引用的ThreadLocal为弱引用,这样能够保证,当ThreadLocal没有其他引用的时候,能够正常被垃圾回收,不会因为Entry的引用,而无法回收。

你可能感兴趣的:(ThreadLocal学习)