ThreadLocal原理

源码解析

ThreadLocal是一个让每个线程都可以存储自己单独的一个变量副本,每个线程只能存储一个变量副本,那么其内部是怎么实现的呢,我们来看看分析下源码
首先其内部是有一个静态类 ThreadLocalMap,这也是最重要的一个部分,首先这个内部类还有一个内部类Entry

    // 这个主要就是存储ThreadLocal的,类似key value进行存储
    static class ThreadLocalMap {
        static class Entry extends WeakReference> {
            Object value;
            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }

其中主要的一个成员变量是,说明了ThreadLocal底层其实是一个Entry数组,其存储的数据都是放到这个里面的

      private Entry[] table;

我们主要来看看ThreadLocal 中 set,get和remove方法是怎么实现的

set方法


    public void set(T value) {
         //获取当前的线程
        Thread t = Thread.currentThread();
        //把t放入getMap中,返回一个ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        //如果不为空
        if (map != null)
           //直接存储到map中,this表示ThreadLocal创建出来的对象本身,value就是要存储的值
            map.set(this, value);
        else
            //否则new一个新的ThreadLocalMap对象存储
            createMap(t, value);
    }

上面主要是调用了这个 map.set(this, value) 方法,下面是源码

 private void set(ThreadLocal key, Object value) {
            Entry[] tab = table;
            int len = tab.length;
            //进行hash运算得出要存储在table数组的下标
            int i = key.threadLocalHashCode & (len-1);
            //判断当前的Entry是否存储为null,为null直接越过for循环
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                 //首先尝试去获取原本的table有没有存储对应的key值
                ThreadLocal k = e.get();
                if (k == key) {
                    //如果有,直接替换返回
                    e.value = value;
                    return;
                }
                 //如果为空,也就是没有,
                if (k == null) {
                     //那么直接存储到table中
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
           //直接new一个新的Entry存储到table数组中
            tab[i] = new Entry(key, value);
           //长度加1
            int sz = ++size;
            //如果满了就对table进行扩容
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

get方法

    public T get() {
         //获取当前线程
        Thread t = Thread.currentThread();
         //得到对应的map
        ThreadLocalMap map = getMap(t);
         //如果不为空
        if (map != null) {
            //得到对应的Entry
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                //返回value值
                T result = (T)e.value;
                return result;
            }
        }
        //如果没有的话,就返回null
        return setInitialValue();
    }

remove方法

     public void remove() {
         //根据当前线程得到对应的map
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             //如果不为空就把ThreadLocal对象本身删除
             m.remove(this);
     }

下面是一个简单使用例子

public class LocalDemo {
    public  static ThreadLocal str = new InheritableThreadLocal<>();
    public static void main(String[] args) {
        str.set("123");
        ThreadLocal local=new ThreadLocal();
        Thread p = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        str.set("234");
                        System.out.println(str.get());
                    }
                }
        );
        p.start();
        try {
            p.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(str.get());
    }
}
//运行结果
234
123

可以看到,返回的结果并不一样。

总结

ThreadLocal内部先是判断当前线程,然后根据当前线程获取到变量,因此不同线程获取到的变量都是不一样的,并且每个ThreadLocal只能保存一个变量副本,如果想要一个线程能够保存多个副本以上,就需要创建多个ThreadLocal。

你可能感兴趣的:(ThreadLocal原理)