ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

简析ThreadLocal

2020-01-29 22:05:38  阅读:237  来源: 互联网

标签:ThreadLocalMap int ThreadLocal value 简析 线程 Entry


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。

绅士jiejie 发布了136 篇原创文章 · 获赞 108 · 访问量 1万+ 私信 关注

标签:ThreadLocalMap,int,ThreadLocal,value,简析,线程,Entry
来源: https://blog.csdn.net/weixin_38106322/article/details/104109398

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有