美文网首页
线程内部存储---TheadLocal从使用到源码分析

线程内部存储---TheadLocal从使用到源码分析

作者: 开飞机的老舒克 | 来源:发表于2017-07-05 17:46 被阅读43次

    ThreadLocal是什么?

    ThreadLocalThreadLocal是一个线程内部用于存储数据的类,通过它可以在指定的线程中存储数据,数据存储以后,只有在该线程中可以获取到存储的数据,对于其它线程来说无法获取到数据。个人认为是一个线程内部的存储机制。

    如何使用?

    ThreadLocal<Boolean> threadLocal = new ThreadLocal<>();
    

    这样就在一个线程创建了ThreadLocal这个对象,这个对象支持范性的,也就是说我们可以存储的自己想存的任意类型。

      threadLocal.set(true);
    

    这样我们就在当前所在线程,存储了true。

    new Thread("Thread#1") {
          @Override
           public void run() {
                System.out.println(threadLocal.get());
               };
            }.start();
    

    我们在一个分线程去打印这个刚才存储的值会发现是null,因为这是在两个线程操作的。如果在当前线程获取则为正确刚才存的值。
    如果想移除这个数据也很简单:

    threadLocal.remove();
    

    这就是threadLocal的使用,其实很简单。 主要记得是区分线程的就ok。

    内部源码是如何实现的呢?

    image.png

    这就是treadLocal中的所有方法了,其实我们最关心的就是set和get方法。

    我们先看set:

     public void set(T value) {
          Thread t = Thread.currentThread();
          ThreadLocalMap map = getMap(t);
             if (map != null)
               map.set(this, value);
             else
               createMap(t, value);
        }
    

    这就是set方法,对就这么几行。
    首先会通过Thread.currentThread();这个方法获取当前线程的Thread。然后通过getMap()获取 ThreadLocalMap对象。

        ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }
    

    getMap中也很简单,就是返回这个线程的threadLocals, 这个ThreadLocal就是 Thread中的一个变量ThreadLocal.ThreadLocalMap threadLocals = null;
    其实还是ThreadLocal中的内部类ThreadLocalMap;这里一会在分析,先顺着思路往下走。
    获取map之后会根据判断如果不是null就进行set,也就是存储,如果是null就会调用createMap()方法进行创建这个ThreadMap。接下来在看是如何set和createMap的。

    创建其实很简单,直接就是new一个。

       void createMap(Thread t, T firstValue) {
            t.threadLocals = new ThreadLocalMap(this, firstValue);
        }
    

    set方法就稍微复杂了些,因为这也是核心内容。set方法在内部类ThreadLocalMap中,所以接下来分析下这个ThreadLocalMap类。

    ThreadLocalMap

    这个类在构造中创建了一个数组, new Entry[INITIAL_CAPACITY]; ,Entry里面就是一个object的对象,然后里面主要getEntry和set方法进行存取和读取。

       private void set(ThreadLocal key, Object value) {
    
                Entry[] tab = table;
                int len = tab.length;
                int i = key.threadLocalHashCode & (len-1);
    
                for (Entry e = tab[i];
                     e != null;
                     e = tab[i = nextIndex(i, len)]) {
                    ThreadLocal k = e.get();
    
                    if (k == key) {//这里判断这个k和传进来的key是否相等
                        e.value = value;//进行存储后return
                        return;
                    }
    
                    if (k == null) {
                        replaceStaleEntry(key, value, i);
                        return;
                    }
                }
    
                tab[i] = new Entry(key, value);
                int sz = ++size;
                if (!cleanSomeSlots(i, sz) && sz >= threshold)
                    rehash();
            }
    
    

    看主要代码 判断k和传进来的key相等话就存储这个value
    get方法:

    private Entry getEntry(ThreadLocal<?> key) {
       int i = key.threadLocalHashCode & (table.length - 1);//根据key获取位下标
                Entry e = table[i];  // 根据下标,获取这个Entry 里面是一个object,实现了软引用
                if (e != null && e.get() == key)
                    return e; //校验没问题后返回
                else
                    return getEntryAfterMiss(key, i, e);
            }
    

    我们再看get:

      public T get() {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null)
                    return (T)e.value;
            }
            return setInitialValue();
        }
    

    get里面也是要先获取当前的Thread这也就是为什么ThreadLocal获取和存储的都是只在当前线程的。

    然后也是getMap方法获取这个ThreadLocalMap,这也就是为什么里面就一行代码,也要写成一个方来,因为这是中思路,其他地方获取直接调用就行,日后扩展的话一样方便。

    再然后就是通过这个map去调用上面说的getEntry方法。

    至此,我们就知道ThreadLocal的总体工作流程和思路了。

    相关文章

      网友评论

          本文标题:线程内部存储---TheadLocal从使用到源码分析

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