• Java ThreadLocal解析


    简介

    ThreadLocal 类似局部变量,解决了单个线程维护自己线程内的变量值(存、取、删),让线程之间的数据进行隔离。(InheritableThreadLocal 特例)

    这里涉及三个类,Thread、ThreadLocal、ThreadLocalMap

    源码解析

    ThreadLocal的结构图

    1. Thread 中有一个 ThreadLocal.ThreadLocalMap 类型的变量 threadLocals。因为ThreadLocalMap变量是跟线程绑定的,所以不存在多线程共享变量之间的并发问题,所以ThreadLocal也就是线程安全的变量。
    2. ThreadLocalMap 是 ThreadLocal 的一个内部静态类,没有继承java.util.Map,定义了一个Entry[]变量,通过Entry的get()方法作为key,value属性作为值来实现一个类似Map的操作
    3. Entry 是 ThreadLocalMap 的一个内部静态类,继承WeakReference<ThreadLocal<?>>,并且定义了一个变量value(Object类型)
    4. ThreadLocal 内部封装了getMap()、Set()、Get()、Remove()4个核心方法,用于操作ThreadLocalMap
    5. 通过getMap()获取每个子线程Thread持有自己的ThreadLocalMap实例,因此它们是不存在并发竞争的
    6. ThreadLocalMap中Entry[]数组存储数据,初始化长度16,大于等于3/4阈值,就进行2倍扩容。
    7. ThreadLocalMap中Entry的key是对ThreadLocal的弱引用,当主线程抛弃掉ThreadLocal对象时,垃圾收集器会忽略这个key的引用而清理掉ThreadLocal对象, 防止了内存泄漏。
    8. 散列算法-魔数0x61c88647,利用一定算法实现了元素的完美散列

    看源码可以得出,set、get、remove操作的都是ThreadLocalMap,key为当前线程,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);
    }
    
    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();
    }
    
    public void remove() {
        ThreadLocalMap m = getMap(Thread.currentThread());
        if (m != null)
            m.remove(this);
    }
    
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    
    

    问题

    不调用remove会内存溢出吗?
    大部分场景下是不会的,少数场景才会。

    运行时,会在栈中产生两个引用,指向堆中相应的对象。
    可以看到,ThreadLocalMap使用ThreadLocal的弱引用作为key,这样一来,假设当ThreadLocal ref和ThreadLocal之间的强引用断开时,即ThreadLocal ref被置为null,下一次GC时,threadLocal对象势必会被回收。
    这样,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,比如使用线程池,线程使用完成之后会被放回线程池中,不会被销毁,这些key为null的Entry的value就会一直存在一条强引用链:
    Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永远无法回收,造成内存泄漏。

    参考资料

    https://www.cnblogs.com/dennyzhangdd/p/7978455.html ThreadLocal终极源码剖析
    https://liwx2000.iteye.com/blog/1774169 ThreadLocal会内存溢出吗
    https://www.jianshu.com/p/cdb2ea3792b5 深入理解Java弱引用

  • 相关阅读:
    Ceres求解直接法BA实现自动求导
    栈溢出漏洞原理详解与利用
    【机器学习】使用CNN神经网络实现对图片分类识别及模型转换
    【Android】java中调用JS的方法
    5 项大奖,70 项满分!阿里云全方位引领云原生技术升级
    分布式系统架构与云原生—阿里云《云原生架构白皮书》导读
    Dubbo-go 发布 1.5 版,朝云原生迈出关键一步
    阿里产品专家:高情商的技术人,如何做沟通?
    CNCF 新增两个孵化项目 | 云原生生态周报 Vol. 58
    掌门教育微服务体系 Solar | 阿里巴巴 Nacos 企业级落地上篇
  • 原文地址:https://www.cnblogs.com/powercto/p/12901068.html
Copyright © 2020-2023  润新知