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;
    }
};

withInitial()

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

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

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);
}

最终的变量放在了线程的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();

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对象无法回收的问题
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值