• ThreadLocal(4):ThreadLocal的核心方法源码


    基于ThreadLocal的内部结构,我们继续分析它的核心方法源码,更深入的了解其操作原理。

    除了构造方法之外, ThreadLocal对外暴露的方法有以下4个:

    方法声明描述
    protected T initialValue()返回当前线程局部变量的初始值
    public void set( T value)设置当前线程绑定的局部变量
    public T get()获取当前线程绑定的局部变量
    public void remove()移除当前线程绑定的局部变量

    ​ 以下是这4个方法的详细源码分析(为了保证思路清晰, ThreadLocalMap部分暂时不展开,下一个知识点详解)

    1 set方法

    (1)源码和对应的中文注释

    1. /**
    2. * 设置当前线程对应的ThreadLocal的值
    3. *
    4. * @param value 将要保存在当前线程对应的ThreadLocal的值
    5. */
    6. public void set(T value) {
    7. // 获取当前线程对象
    8. Thread t = Thread.currentThread();
    9. // 获取此线程对象中维护的ThreadLocalMap对象
    10. ThreadLocalMap map = getMap(t);
    11. // 判断map是否存在
    12. if (map != null)
    13. // 存在则调用map.set设置此实体entry
    14. map.set(this, value);
    15. else
    16. // 1)当前线程Thread 不存在ThreadLocalMap对象
    17. // 2)则调用createMap进行ThreadLocalMap对象的初始化
    18. // 3)并将 t(当前线程)和value(t对应的值)作为第一个entry存放至ThreadLocalMap中
    19. createMap(t, value);
    20. }
    21. /**
    22. * 获取当前线程Thread对应维护的ThreadLocalMap
    23. *
    24. * @param t the current thread 当前线程
    25. * @return the map 对应维护的ThreadLocalMap
    26. */
    27. ThreadLocalMap getMap(Thread t) {
    28. return t.threadLocals;
    29. }
    30. /**
    31. *创建当前线程Thread对应维护的ThreadLocalMap
    32. *
    33. * @param t 当前线程
    34. * @param firstValue 存放到map中第一个entry的值
    35. */
    36. void createMap(Thread t, T firstValue) {
    37. //这里的this是调用此方法的threadLocal
    38. t.threadLocals = new ThreadLocalMap(this, firstValue);
    39. }

    (2)代码执行流程

    • ​首先获取当前线程,并根据当前线程获取一个Map
    • 如果获取的Map不为空,则将参数设置到Map中(当前ThreadLocal的引用作为key)
    • 如果Map为空,则给该线程创建 Map,并设置初始值

    2 get方法

    (1)源码和对应的中文注释

    1. /**
    2. * 返回当前线程中保存ThreadLocal的值
    3. * 如果当前线程没有此ThreadLocal变量,
    4. * 则它会通过调用{@link #initialValue} 方法进行初始化值
    5. *
    6. * @return 返回当前线程对应此ThreadLocal的值
    7. */
    8. public T get() {
    9. // 获取当前线程对象
    10. Thread t = Thread.currentThread();
    11. // 获取此线程对象中维护的ThreadLocalMap对象
    12. ThreadLocalMap map = getMap(t);
    13. // 如果此map存在
    14. if (map != null) {
    15. // 以当前的ThreadLocal 为 key,调用getEntry获取对应的存储实体e
    16. ThreadLocalMap.Entry e = map.getEntry(this);
    17. // 对e进行判空
    18. if (e != null) {
    19. @SuppressWarnings("unchecked")
    20. // 获取存储实体 e 对应的 value
    21. // 即为我们想要的当前线程对应此ThreadLocal的值
    22. T result = (T)e.value;
    23. return result;
    24. }
    25. }
    26. /*
    27. 初始化 : 有两种情况有执行当前代码
    28. 第一种情况: map不存在,表示此线程没有维护的ThreadLocalMap对象
    29. 第二种情况: map存在, 但是没有与当前ThreadLocal关联的entry
    30. */
    31. return setInitialValue();
    32. }
    33. /**
    34. * 初始化
    35. *
    36. * @return the initial value 初始化后的值
    37. */
    38. private T setInitialValue() {
    39. // 调用initialValue获取初始化的值
    40. // 此方法可以被子类重写, 如果不重写默认返回null
    41. T value = initialValue();
    42. // 获取当前线程对象
    43. Thread t = Thread.currentThread();
    44. // 获取此线程对象中维护的ThreadLocalMap对象
    45. ThreadLocalMap map = getMap(t);
    46. // 判断map是否存在
    47. if (map != null)
    48. // 存在则调用map.set设置此实体entry
    49. map.set(this, value);
    50. else
    51. // 1)当前线程Thread 不存在ThreadLocalMap对象
    52. // 2)则调用createMap进行ThreadLocalMap对象的初始化
    53. // 3)并将 t(当前线程)和value(t对应的值)作为第一个entry存放至ThreadLocalMap中
    54. createMap(t, value);
    55. // 返回设置的值value
    56. return value;
    57. }

    (2)代码执行流程

    • 首先获取当前线程, 根据当前线程获取一个Map
    • 如果获取的Map不为空,则在Map中以ThreadLocal的引用作为key来在Map中获取对应的Entry e,否则转到D
    • 如果e不为null,则返回e.value,否则转到D
    • Map为空或者e为空,则通过initialValue函数获取初始值value,然后用ThreadLocal的引用和value作为firstKey和firstValue创建一个新的Map

    总结: 先获取当前线程的 ThreadLocalMap 变量,如果存在则返回值,不存在则创建并返回初始值

    3 remove方法

    (1)源码和对应的中文注释

    1. /**
    2. * 删除当前线程中保存的ThreadLocal对应的实体entry
    3. */
    4. public void remove() {
    5. // 获取当前线程对象中维护的ThreadLocalMap对象
    6. ThreadLocalMap m = getMap(Thread.currentThread());
    7. // 如果此map存在
    8. if (m != null)
    9. // 存在则调用map.remove
    10. // 以当前ThreadLocal为key删除对应的实体entry
    11. m.remove(this);
    12. }

    (2)代码执行流程

    • 首先获取当前线程,并根据当前线程获取一个Map
    • 如果获取的Map不为空,则移除当前ThreadLocal对象对应的entry

    4 initialValue方法

    1. /**
    2. * 返回当前线程对应的ThreadLocal的初始值
    3. * 此方法的第一次调用发生在,当线程通过get方法访问此线程的ThreadLocal值时
    4. * 除非线程先调用了set方法,在这种情况下,initialValue 才不会被这个线程调用。
    5. * 通常情况下,每个线程最多调用一次这个方法。
    6. *
    7. *

      这个方法仅仅简单的返回null {@code null};

    8. * 如果程序员想ThreadLocal线程局部变量有一个除null以外的初始值,
    9. * 必须通过子类继承{@code ThreadLocal} 的方式去重写此方法
    10. * 通常, 可以通过匿名内部类的方式实现
    11. *
    12. * @return 当前ThreadLocal的初始值
    13. */
    14. protected T initialValue() {
    15. return null;
    16. }

    ​ 此方法的作用是 返回该线程局部变量的初始值。

    • 这个方法是一个延迟调用方法,从上面的代码我们得知,在set方法还未调用而先调用了get方法时才执行,并且仅执行1次。
    • 这个方法缺省实现直接返回一个null。
    • 如果想要一个除null之外的初始值,可以重写此方法。(备注: 该方法是一个protected的方法,显然是为了让子类覆盖而设计的)

  • 相关阅读:
    CoCube群机器人预览→资讯剧透←
    Open3D 点云区域生长分割算法
    Kernel Memory 入门系列:生成并获取文档摘要
    数据类型的定义
    GPU如何成为AI的加速器
    【毕设选题】深度学习人体跌倒检测 -yolo 机器视觉 opencv python
    Java面向对象02:回顾方法的定义
    jprofiler使用
    【数据结构初阶】Leetcode二叉树基础练习&&完全二叉树判断
    node插件MongoDB(四)—— 库mongoose 的文档操作使用
  • 原文地址:https://blog.csdn.net/u013938578/article/details/136187645