ThreadLocal原理

ThreadLocal原理

  1. ThreadLocal对象new出来存放到堆中,ThreadLocal引用是存放在栈里
  1. Thread 类有个 ThreadLocalMap 成员变量,Map的key是Threadlocal 对象,value是你要存放的线程局部变量。

    public void set(T value) {
    //获取当前线程Thread,就是上图画的Thread 引用
    Thread t = Thread.currentThread();
    //Thread类有个成员变量ThreadlocalMap,拿到这个Map
    ThreadLocalMap map = getMap(t);
    if (map != null)
    //this指的就是Threadlocal对象
    map.set(this, value);
    else
    createMap(t, value);
    }

    ThreadLocalMap getMap(Thread t) {
    //获取线程的ThreadLocalMap
    return t.threadLocals;
    }

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

  2. ThreadLocalMap 类的定义在 Threadlocal中。结构与HashMap一样

  3. 使用ThreadLocal后一定手动remove掉 引用ThreadLocal的对象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收。value在下一次ThreadLocalMap调用set、get、remove的时候会被清除。

  1. ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。

既然是线程局部变量,那为什么不用线程对象(Thread对象)作为key,这样不是更清晰,直接用线程作为key获取线程变量?

  1. 这样设计会有个问题,比如: 我已经把用户信息存在线程变量里了,这个时候需要新增加一个线程变量,比方说新增用户地理位置信息
  2. 我们ThreadlocalMap 的key用的是线程,再存一个地理位置信息,key都是同一个线程(key一样),会覆盖原来的用户信息。

为什么ThreadLocal中的key设计成WeakReference

复制代码
ThreadLocal<UserInfo> userInfoThreadLocal = new ThreadLocal<>();
userInfoThreadLocal.set(userInfo);
  1. 为了尽大可能的避免内存泄漏
  2. new出的ThreadLocal被userInfoThreadLocal强引用,同时也被ThreadLocalMap的Key弱引用
  3. gc要回收ThreadLocal对象的前提是它只被WeakReference引用
  4. 所以只要ThreadLocal对象如果还被 userInfoThreadLocal(强引用) 引用着,GC是不会回收被WeakReference引用的对象的。
  5. 有些线程会用线程池优化,线程生命周期很长,根据JVM 根搜索算法,一直存在 Thread -> ThreadLocalMap -> Entry(元素)这样一条引用链路,如果key不设计成WeakReference类型,是强引用的话,就一直不会被GC回收,key就一直不会是null,不为null Entry元素就不会被清理(ThreadLocalMap是根据key是否为null来判断是否清理Entry)
  6. 所以ThreadLocal的设计者认为只要ThreadLocal 所在的作用域结束了工作被清理了,GC回收的时候就会把key引用对象回收,key置为null,ThreadLocal会尽力保证Entry清理掉来最大可能避免内存泄漏。
  7. ThreadLocalMap中Entry 继承了WeakReference类,Entry 中的 key 是WeakReference类型的,在Java 中当对象只被 WeakReference 引用,没有其他对象引用时,被WeakReference 引用的对象发生GC 时会直接被回收掉。
相关推荐
ttod_qzstudio8 分钟前
【软考算法】软件设计师下午第四题之动态规划:0-1 背包与最长公共子序列的“填表艺术“
算法·动态规划·软考
柒和远方13 分钟前
LeetCode 139. 单词拆分 —— 从暴力回溯到 DP 完全背包
javascript·python·算法
漫随流水31 分钟前
Java——springboot web案例
java·开发语言·spring boot
从此以后自律39 分钟前
Java Object 类常用方法全讲解
java·开发语言
念何架构之路1 小时前
Docker 容器状态机 学习
java·学习·docker
你有我备注吗1 小时前
SQL学习之查询
java·sql·学习
从零开始的代码生活_1 小时前
C++ stack、queue 与 priority_queue:容器适配器原理与实战
开发语言·c++·后端·学习·算法
晚笙coding1 小时前
LeetCode 226. 翻转二叉树(Invert Binary Tree)
算法·leetcode·职场和发展
2zcode1 小时前
项目文档:基于MATLAB低采样率ISAR成像的快速稀疏重建算法研究
开发语言·算法·matlab
xixi09242 小时前
JMeter5.6.3基础使用教程1
java·开发语言