zoukankan      html  css  js  c++  java
  • ThreadLocal原理详细解析

    1.ThreadLocal概念

    ThreadLocal,可以叫做线程本地变量或线程本地存储,顾名思义就是ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。其实就是通过空间换时间的方式来取得对每个线程各自变量的共享。

    变量值的共享可以使用 public static 变量的形式,所有的线程都使用同一个被 public static 修饰的变量。ThreadLocal主要解决的就是每个线程绑定自己的值,可以将ThreadLocal类比喻成全 局存放数据的盒子,盒子中可以存储每个线程的私有变量。

    2.具体源码分析

    • ThreadLocal有以下方法:

    •  方法的具体实现:
      • 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();
            }

        1.获取当前线程t;2.通过getMap方法获取一个ThreadLocalMap类型的map;3.获取这个键值对,注意这里获取键值对传进去的是  this,而不是当前线程t。

        如果获取成功,则返回value值。如果map为空,则调用setInitialValue方法返回value。

        • getMap方法:

          ThreadLocalMap getMap(Thread t) {
                  return t.threadLocals;
              }
          
          //Thread类
          ThreadLocal.ThreadLocalMap threadLocals = null;

          根据传入的当前线程t返回它的局部变量threadLocals,而这个threadLocals实际上是ThreadLocalMap类型的,而ThreadLocalMap又是ThreadLocal的一个内部类。

        • ThreadLocalMap:

          static class ThreadLocalMap {
          
                  /**
                   * The entries in this hash map extend WeakReference, using
                   * its main ref field as the key (which is always a
                   * ThreadLocal object).  Note that null keys (i.e. entry.get()
                   * == null) mean that the key is no longer referenced, so the
                   * entry can be expunged from table.  Such entries are referred to
                   * as "stale entries" in the code that follows.
                   */
                  static class Entry extends WeakReference<ThreadLocal<?>> {
                      /** The value associated with this ThreadLocal. */
                      Object value;
          
                      Entry(ThreadLocal<?> k, Object v) {
                          super(k);
                          value = v;
                      }
                  }

          ThreadLocalMap中的Entry继承了WeakReference,并且使用ThreadLocal类型的值作为key。

        • 接下来看get方法中返回的setInitialValue方法:

          /**
               * Variant of set() to establish initialValue. Used instead
               * of set() in case user has overridden the set() method.
               *
               * @return the initial value
               */
              private T setInitialValue() {
                  T value = initialValue();
                  Thread t = Thread.currentThread();
                  ThreadLocalMap map = getMap(t);
                  if (map != null)
                      map.set(this, value);
                  else
                      createMap(t, value);
                  return value;
              }

          当map不为空时,将键值对存进去,当map为空时,重新创建一个map

        • protected T initialValue() {
                  return null;
              }
          initialValue方法默认返回空,所以要先调用set方法才可以调用get方法,如果想要先调用get方法的话,需要重写initialValue方法。
        • createMap方法:

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

    到这里基本就可以看出ThreadLocal是如何为每个线程创建变量的副本的:

    首先,在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,key为当前ThreadLocal变量,value为变量副本(即T类型的变量)。

    初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。

    然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。

    • 例子:
    public class Test {
        ThreadLocal<Long> longLocal = new ThreadLocal<Long>();
        ThreadLocal<String> stringLocal = new ThreadLocal<String>();
     
         
        public void set() {
            longLocal.set(Thread.currentThread().getId());
            stringLocal.set(Thread.currentThread().getName());
        }
         
        public long getLong() {
            return longLocal.get();
        }
         
        public String getString() {
            return stringLocal.get();
        }
         
        public static void main(String[] args) throws InterruptedException {
            final Test test = new Test();
             
             
            test.set();
            System.out.println(test.getLong());
            System.out.println(test.getString());
         
             
            Thread thread1 = new Thread(){
                public void run() {
                    test.set();
                    System.out.println(test.getLong());
                    System.out.println(test.getString());
                };
            };
            thread1.start();
            thread1.join();
             
            System.out.println(test.getLong());
            System.out.println(test.getString());
        }
    }

     这段代码的输出结果为: 

    从运行结果看出:因为实例化了两个ThreadLocal变量,所以他们对各自的局部变量保存的副本值是不一样的,因此在两个线程中的执行并不会互相影响各自的变量值。

    总结一下:

      1)实际的通过ThreadLocal创建的副本是存储在每个线程自己的threadLocals中的;

      2)为何threadLocals的类型ThreadLocalMap的键值为ThreadLocal对象,因为每个线程中可有多个threadLocal变量,就像上面代码中的longLocal和stringLocal;

      3)在进行get之前,必须先set,否则会报空指针异常;

          如果想在get之前不需要调用set就能正常访问的话,必须重写initialValue()方法,设置默认值。

        因为在上面的代码分析过程中,我们发现如果没有先set的话,即在map中查找不到对应的存储,则会通过调用setInitialValue方法返回i,而在setInitialValue方法中,有一个语句是T value = initialValue(), 而默认情况下,initialValue方法返回的是null。

    3.值继承

    使用InheritableThreadLocal类可以实现值的继承,让子线程从父线程中取得值。不过要注意,如果子线程在取得值的同时,父线程将InheritableThreadLocal中的值进行修改,那么子线程取到的还是原来的值。

    https://www.cnblogs.com/dolphin0520/p/3920407.html

  • 相关阅读:
    第60届IMO 第5题
    第31届IMO 第2题
    洛谷【P1595 信封问题】 题解
    洛谷【P2022 有趣的数】 题解
    洛谷【P5004 专心OI
    04-----jQuery的属性操作
    03-----jQuery动画效果
    02-----jQuery的选择器
    01-----jQuery介绍
    17-----案例
  • 原文地址:https://www.cnblogs.com/baichendongyang/p/13235527.html
Copyright © 2011-2022 走看看