• ThreadLocal


    ThreadLocal

    面试题

    1. ThreadLocal中的ThreadLocalMap的数据结构和关系
    2. ThreadLocal的key是弱引用,这是为什么
    3. ThreadLocal内存泄漏问题
    4. ThreadLocal中最后为什么要加remove方法

    简介

    ThreadLocal提供线程局部变量,这些变量与正常的变量不一样,每一个线程访问ThreadLocal实例的时候都有自己的,独立初始化的变量副本.ThreadLocal实例通常是类中的私有静态字段.

    实现每一个线程都有自己专属的本地变量副本 (自己用自己的变量,不和其他线程共享),避免了线程问题.

    API介绍

    initialValue()

    protect T initialValue() 返回此线程局部变量的当前线程的初始值,通常使用匿名内部类,不推荐使用

    为每个线程设置初始值

    ThreadLocal<Integer> saleVolume = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    withInitial()

    为每个线程设置初始值 推荐使用这种方式对每个线程进行初始化赋值

    ThreadLocal<Integer> saleVolume = ThreadLocal.withInitial(() -> 0);
    
    • 1

    set()

    设置当前线程的变量副本值

    get()

    获取当前线程的变量副本值

    remove()

    删除此线程局部变量的当前线程值

    注:必须回收自定义的ThreadLocal变量,尤其是在线程池的场景下,线程经常会被复用,如果不清理自定义的ThreadLocal变量,可能会影响后续业务逻辑和造成内存泄漏问题

    源理分析

    问题解决:Thread ThreadLocal ThreadLocalMap之间的关系

    线程类中包含了ThreadLocal ,ThreadLocal 中包含了ThreadLocalMap静态内部类

    在这里插入图片描述

    ThreadLocal是一个泛型类,保证可以接受任何类型的对象,ThreadLocal中维护了一个Map的静态内部类,我们使用set,get方法其实都是这个ThreadLocalMap类中对应的get,set方法

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    最终的变量放在了线程的ThreadLocalMap中,并不是存在ThreadLocal上,ThreadLocal作为key

    强软弱虚四个引用

    强引用:无论任何情况下,只要强引用关系还存在,垃圾回收器就不会回收该对象,例如Object o = new Object();这种引用关系为强引用

    软引用:在将发生内存溢出之前,将会把这些对象列为回收范围,垃圾回收时并且达到内存溢出情况会将这些对象进行回收

    弱引用:在将发生垃圾回收时,不管会不会发生内存溢出,都会将此类对象进行垃圾回收

    虚引用:表示已经回收的对象,为对象设置虚引用关联的唯一目的就是在这个对象被垃圾收集器时收到一个系统通知

    Key键问什么要用弱引用

    ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal的外部强引用为null了,此时ThreadLocal的外部强引用一定会被垃圾回收掉

    ThreadLocal<String> s = new ThreadLocal<>();
    s.set("hello");
    s.get();
    
    • 1
    • 2
    • 3

    s.set()方法建立一个entry节点,假设方法执行完,栈帧被销毁,s也就没有了,但是此时ThreadLocalMap中的entry中的key引用还指向这个对象,如果这个key为强引用,就会导致key指向ThreadLocal对象不能被垃圾回收,造成内存泄漏

    在这里插入图片描述

    清除脏Entry

    这个key为弱引用就大概率的减少内存泄漏问题,ThreadLocal对象在方法执行完毕后顺利被回收且Entry的key引用指向就为null

    当我们往ThreadLocal变量赋值,实际上就是当前的Entry(ThreadLocal为key,value为值)往ThreadLocalMap中存放,Entry中的key是弱引用,当threadLocal外部引用被置为null,当GC的时候,这个ThreadLocal就会被回收,这时key为null,value还存在,就没有办法访问这些key为null的Entry值,如果线程不结束,这时存在很多key为null的entry,value无法回收,造成内存回收(线程复用 线程池)

    调用get,set,remove方法时,就会尝试删除key为null的entry,可以是否value对象占用的内存

    总结

    ThreadLocal通常用static关键字修饰,ThreadLocal能实现线程隔离,不在于它自己本身,而是在于Thread的ThreadLocalMap,所以,ThreadLocal可以只初始化一次,只分配一块存储空间,没必要作为成员变量多次被初始化

    • ThreadLocal并不解决线程间共享数据问题
    • 适用于变量在线程间隔离且在方法间共享的场景
    • 每个线程持有一个只属于自己的专属map并维护了ThreadLocal对象与具体实例的映射
    • ThreadLocalMap的Entry对ThreadLocal是弱引用,避免了ThreadLocal对象无法回收的问题
  • 相关阅读:
    SR和GBN的区别
    有梦想就去追,程序员辞职组乐队被老板资助
    学习知识汇总(持续更新......)
    使用 Azure 静态 Web 应用服务免费部署 Hexo 博客
    在线问诊 Python、FastAPI、Neo4j — 创建药品节点
    FCOS论文复现:通用物体检测算法
    For循环控制
    STM32 4位数码管和74HC595
    【机械】基于Matlab求解Hoek-Brown应变软化岩体GRC曲线
    php static延迟静态绑定
  • 原文地址:https://blog.csdn.net/weixin_48304611/article/details/125459107