简析ThreadLocal

二次信任 提交于 2020-01-30 00:05:48

ThreadLocal是什么

我们知道在使用多线程时,有时需要共享变量,需要同步数据,所以各个线程之间对这个变量都是开放的,这概念可以称之为线程开放。但也不是所有时候都需要同步数据,每个线程也可以有自己独有的变量,不需要同步给其他线程,这概念可以叫做线程封闭。而ThreadLocal就是线程封闭具体的体现之一。在JDK1.2版本里就提供了java.lang.ThreadLocal类,我们可以把ThreadLocal理解为线程的局部变量,是一个线程级别的变量。

ThreadLocal的简单实践

demo代码如下:

public class TestMain {
    public static void main(String[] args) throws Exception {
        ThreadLocal<String> value = new ThreadLocal<>();
        value.set("主线程");
        String threadLocal = value.get();
        System.out.println("执行线程一前主线程threadLocal值为:" + threadLocal);
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                String threadLocal = value.get();
                System.out.println("设置线程一threadLocal值前,值为:" + threadLocal);
                value.set("线程一");
                String threadLocal1 = value.get();
                System.out.println("设置线程一threadLocal值后,值为:" + threadLocal1);
            }
        });
        thread.start();
        thread.join();
        System.out.println("线程一运行完后,主线程threadLocal值为" + threadLocal);
    }
}

运行结果如下:
在这里插入图片描述
可以看到主线程的ThreadLocal的值一直是"主线程",线程一在没设置值前是null,设置后为"线程一",两个线程的ThreadLocal值互不影响,各自为政。

ThreadLocal的get()方法的源码分析

get()方法源码:

 public T get() {
        //获取当前线程
        Thread t = Thread.currentThread();
        //以线程为参数获取ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            //从map里拿到线程对应的ThreadLocalMap.Entry对象
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                //获取ThreadLocalMap.Entry对象里的value值
                T result = (T)e.value;
                //返回结果
                return result;
            }
        }
        //如果该线程的ThreadLocal值为空,则设置初始值返回
        return setInitialValue();
    }

ThreadLocalMap类源码:

  static class ThreadLocalMap {
        //静态内部类Entry
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

        /**
         * The initial capacity -- MUST be a power of two.
         */
        private static final int INITIAL_CAPACITY = 16;


        //Entry数组
        private Entry[] table;

        /**
         * The number of entries in the table.
         */
        private int size = 0;

        /**
         * The next size value at which to resize.
         */
        private int threshold; // Default to 0

        /**
         * Set the resize threshold to maintain at worst a 2/3 load factor.
         */
        private void setThreshold(int len) {
            threshold = len * 2 / 3;
        }

        /**
         * Increment i modulo len.
         */
        private static int nextIndex(int i, int len) {
            return ((i + 1 < len) ? i + 1 : 0);
        }

        /**
         * Decrement i modulo len.
         */
        private static int prevIndex(int i, int len) {
            return ((i - 1 >= 0) ? i - 1 : len - 1);
        }

getMap()方法源码:

//threadLocals变量是ThreadLocalMap类
ThreadLocal.ThreadLocalMap threadLocals = null;
//返回ThreadLocalMap对象
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

getEntry()方法源码:

private Entry getEntry(ThreadLocal<?> key) {
            //通过threadLocalHashCode进行一个位运算(取模)得到索引i
            int i = key.threadLocalHashCode & (table.length - 1);
            //通过索引值获取Entry实例
            Entry e = table[i];
            //满足条件下返回Entry,否则走另一个分支
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }

getEntryAfterMiss()方法源码:

  private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
        Entry[] tab = table;
        int len = tab.length;
        //Entry不为空时做的逻辑
        while (e != null) {
            ThreadLocal<?> k = e.get();
            if (k == key)
                return e;
            if (k == null)
                expungeStaleEntry(i);
            else
                i = nextIndex(i, len);
            e = tab[i];
        }
        //返回空值
        return null;
    }

setInitialValue()方法源码:

 private T setInitialValue() {
        //返回null
        T value = initialValue();
        //取得当前线程
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            //如果ThreadLocalMap对象不为空,则为当前ThreadLocal对象设置对应value值
            map.set(this, value);
        else
            //创建ThreadLocalMap为当前线程存储ThreadLocal的value值
            createMap(t, value);
        return value;
    }
    //初始化值为null
 protected T initialValue() {
        return null;
    }        

set()方法源码:

private void set(ThreadLocal<?> key, Object value) {
            Entry[] tab = table;
            int len = tab.length;
            //通过threadLocalHashCode进行一个位运算(取模)得到索引i
            int i = key.threadLocalHashCode & (len-1);
            //满足括号里条件的设值逻辑
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();
                //如果入参的ThreadLocal实例key和k相等,则把入参的value值设置为这个Entry的value值
                if (k == key) {
                    e.value = value;
                    return;
                }
                //k为空逻辑
                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
            //以入参key和value为参数,创建新的Entry对象,赋值给下标为i的Entry对象
            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
        

createMap源码:

void createMap(Thread t, T firstValue) {
        //创建当前线程的ThreadLocalMap对象实例threadLocals,并赋值
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

从以上源码可以知道,ThreadLocal里面有个静态内部类ThreadLocalMap,ThreadLocalMap类里面维护了一个Entry数组,我们的get()方法就是先以当前线程为参数取得ThreadLocalMap对象,然后再以ThreadLocal为参数通过threadLocalHashCode进行一个位运算(取模)得到索引i,接着通过索引i从该对象的属性Entry数组里面取出对应下标的value。而如果线程没有对应的ThreadLocalMap,或者ThreadLocalMap对象没有对应的Entry对象,那么就会进入初始化方法setInitialValue()方法,这个方法里会先初始化value值为空,然后判断当前线程的ThreadLocalMap对象是否为空,不为空,则调用set()方法设值,为空则调用createMap()方法设值,而这里我们要知道的是无论是走哪套设值逻辑,设置的值都是初始化的value值null,这也就能解释为什么线程一开始调用get()方法时,得到的值是null了。

总结

殊途同归,我相信通过对get()方法的简单分析,我们也能够对ThreadLocal类里的一些变量和方法有一定的理解。所以如果数据是以线程为作用域,且要求每个线程有自己的副本时,类似于会话等,那么我们可以选择使用ThreadLocal。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!