美文网首页
ThreadLocal学习

ThreadLocal学习

作者: 仰望forward | 来源:发表于2020-05-21 16:28 被阅读0次

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

    ThreadLocal原理

    ThreadLocal

    代码分析

    从demo说起

    /*
    * 持有ThreadLocal的类
    **/
    public class ThreadLocalContext {
    
        private static final ThreadLocal<String> NAME_LOCAL = new ThreadLocal<>();
        private static final ThreadLocal<Integer> 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学习

          本文链接:https://www.haomeiwen.com/subject/ysctahtx.html