了解ThreadLocal

Posted ninggrow

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了了解ThreadLocal相关的知识,希望对你有一定的参考价值。

ThreadLocal是什么

ThreadLocal提供了一个线程内的局部变量。这种变量在多线程环境下访问能够保证各个线程里的变量相对独立于其他线程内的变量。
只要线程存活并且ThreadLocal实例可以访问,每个线程都保存对其线程局部变量副本的隐式引用; 线程消失后,线程本地实例的所有副本都将被垃圾收集,除非存在对这些副本的其他引用。

ThreadLocal的实现原理

ThreadLocal的基本操作都是对ThreadLocalMap的一些操作实现,而线程的局部变量就是保存在ThreadLocalMap中的。在Thread类中存在一个ThreadLocal.ThreadLocalMap类型的变量threadLocals,在Thread类中对threadLocals的注释是这样的:

/* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

这个变量是通过ThreadLocal进行维护的。
可以看出来,线程的局部变量不是存放在ThreadLocal实例里边的,而是存放在Thread的threadLocals变量里边,只是通过ThreadLocal对线程的局部变量的进行维护。

ThreadLocal的基本操作

  • void set(T value)

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

    首先获取当前线程,然后获取当前线程内的ThreaLocalMap:

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

    只是简单的返回Thread内的threadLocals,第一次调用时,threadLocals为null,通过createMap创建Thread的变量threadLocals:

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

    如果threadLocals不为null,就把变量放入当前线程的threadLocals

  • T get()

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

    这些代码都比较简单,就不再详述了。需要说明的是,在ThreadLocalMap中获取不到值的话,会通过在setInitialValue()方法中调用initialValue()获取初始化值设置给ThreadLocalMap:

      protected T initialValue() {
      return null;
      }

    大概可以明白,在我们创建ThreadLocal的时候,可以通过覆盖initialValue()给线程内的局部变量一个默认的初始化值。

  • void remove()

      public void remove() {
       ThreadLocalMap m = getMap(Thread.currentThread());
       if (m != null)
           m.remove(this);
       }
  • withInitial()
    在JDK8中提供这样一个静态方法

      public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
      return new SuppliedThreadLocal<>(supplier);
      }

    通过这个静态方法能够创建一个可以返回初始值的ThreaLocal,这个方法是为了满足JDK8的函数式编程方式,创建ThreadLocal还是挺方便的。

需要注意的是,ThreadLocalMap是使用ThreadLocal的弱引用WeakReference作为key的。为了防止内存泄露,在调用getEntry方法和set方法时,ThreadLocal的直接索引位置获取到Entry e,如果e不为null,而key = e.get()为null或者不一致,就进行一些操作:如果key==null,则直接擦除该位置上的Entry;如果key不一致,查找下一个位置上的Entry e,直到这个e对应的key一致返回e,在这个过程中,key==null的Entry都会被擦除。

以上是关于了解ThreadLocal的主要内容,如果未能解决你的问题,请参考以下文章

我对ThreadLocal的理解

Android ThreadLocal类浅析

Android进阶你必须要了解的知识:ThreadLocal

广州某大公司:ThreadLocal了解吗

带你掌握被大厂面试官狂轰乱炸ThreadLocal原理,别再傻乎乎的说不了解了~~~

并发编程大师系列之:你真的了解ThreadLocal吗?