1. 本篇文章主要讲述ThreadLocal是如何实现线程本地变量的。<br />参考:冰河-高并发笔记<br /> CodeSheep-[https://mp.weixin.qq.com/s/ND-nUCGvXTHkEClKqN1qrQ](https://mp.weixin.qq.com/s/ND-nUCGvXTHkEClKqN1qrQ)

概述

解决线程安全问题的方法主要有阻塞同步、非阻塞同步和无同步方法(深入理解JVM 3版)

  • 阻塞同步:synchronized和ReentrantLock
  • 非阻塞同步:CAS
  • 无同步方法:使用ThreadLocal

ThreadLocal将变量私有化,于是线程间没有共享数据,就不存在什么线程安全问题了

ThreadLocal原理探究

  • 线程独享的变量存储在哪里?
  • ThreadLocal的set()、get()、remove()方法的源码
  • 如何让子线程获取父线程中设置的值

    线程独享变量存储在哪里?

    答:变量存储在该线程的Thread类实例中。

  1. public class Thread implements Runnable {
  2. // 省略代码....
  3. /* ThreadLocal values pertaining to this thread. This map is maintained
  4. * by the ThreadLocal class. */
  5. ThreadLocal.ThreadLocalMap threadLocals = null;
  6. // 省略代码....
  7. }
  • ThreadLocal可以看成一个工具类
  • 具体的作用就是操作当前线程的私有变量

    ThreadLocal的方法源码分析

    set()方法

    ```java public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null)
    1. map.set(this, value);
    else
    1. createMap(t, value);
    }
  1. void createMap(Thread t, T firstValue) {
  2. t.threadLocals = new ThreadLocalMap(this, firstValue);
  3. }
  1. - 当前线程调用 `ThreadLocal.set(xxx)`
  2. - `set()`方法获取当前是哪个线程调用的(这里调用线程假设为A
  3. - 从线程A中获取它的`ThreadLocal.ThreadLocalMap threadLocals`的值
  4. - 为空,调用`createMap()` 为线程A创建一个
  5. - 不为空,修改原`threadLocals`成员变量的值
  6. <a name="XQGlJ"></a>
  7. ### get()方法
  8. ```java
  9. public T get() {
  10. Thread t = Thread.currentThread();
  11. ThreadLocalMap map = getMap(t);
  12. if (map != null) {
  13. ThreadLocalMap.Entry e = map.getEntry(this);
  14. if (e != null) {
  15. @SuppressWarnings("unchecked")
  16. T result = (T)e.value;
  17. return result;
  18. }
  19. }
  20. return setInitialValue();
  21. }
  22. private T setInitialValue() {
  23. T value = initialValue();
  24. Thread t = Thread.currentThread();
  25. ThreadLocalMap map = getMap(t);
  26. if (map != null)
  27. map.set(this, value);
  28. else
  29. createMap(t, value);
  30. return value;
  31. }
  • 线程A调用ThreadLocal.get()方法
  • 获取当前调用线程(这里线程为线程A)
  • 从线程A中获取threadLocals成员变量
  • threadLocals不为空且threadLocals(可以看作一个Map)的里面值也不为空,则从中获取值
  • 否则就是初始化这个threadLocals并设置一个this->null 键值对

    remove()方法

    1. public void remove() {
    2. ThreadLocalMap m = getMap(Thread.currentThread());
    3. if (m != null)
    4. m.remove(this);
    5. }
  • 线程A调用ThreadLocal.remove()

  • 获取当前调用线程(线程A)的threadLocals
  • threadLocals不为空,清除里面的key

    子线程获取父线程的值

    通过ThreadLocal设置的值是不具备传递效果的,但是可以使用其子类InheritableThreadLocal来实现值的传递。

    InheritableThreadLocal的原理探究

    父线程在new Thread之前通过InheritableThreadLocal.set()设置值可以被新new出来的这个线程所拿到,并复制到新线程中

    1. public class Thread implements Runnable {
    2. // 省略代码....
    3. /* ThreadLocal values pertaining to this thread. This map is maintained
    4. * by the ThreadLocal class. */
    5. ThreadLocal.ThreadLocalMap threadLocals = null;
    6. /*
    7. * InheritableThreadLocal values pertaining to this thread. This map is
    8. * maintained by the InheritableThreadLocal class.
    9. */
    10. ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    11. // 省略代码....
    12. }
  • 在Thread中还有一个和threadLocals类型一致的成员变量inheritableThreadLocals

  • 通过ThreadLocal设置的线程变量存储在threadLocals
  • 通过InheritableThreadLocal设置的线程变量存储在InheritableThreadLocals

    方法探究

    这里我就不列举InheritableThreadLocalset()get()remove()方法的具体代码了。
    该类是ThreadLocal的子类,上述的三个方法均是ThreadLocal类的实现,唯一不同的是InheritableThreadLocal重写了其中获取调用线程中的ThreadLocal.ThreadMap的方法。

  • ThreadLocal获取且操作 threadLocals

  • InheritableThreadLocal则是操作inheritableThreadLocals

    如何传递

  • 本质:将父线程的inheritableThreadLocals成员变量赋值给子线程的inheritableThreadLocals

  • 发生位置:new Thread()的构造方法执行过程中完成

    注意

    作者认为,在new完子线程后,父线程对inheritableThreadLocals变量的修改失不会影响子线程的

ThreadLocal内存泄漏

待补充