ThreadLocal的理解

ThreadLocal的理解

为什么ThreadLocal的key为弱引用?

首先,我们需要看看ThreadLocal是如何存储数据的
ThreadLocal的基本用法

public class ThreadLocalExample {

    private static final ThreadLocal<String> THREAD_LOCAL = ThreadLocal.withInitial(() -> "test");

    public static void main(String[] args) {
        // 或者在使用的时候进行set
        ThreadLocal<String> threadLocal = new ThreadLocal<>();
        threadLocal.set("test");
        System.out.println(threadLocal.get());
    }

    private static void threadLocalExample() {
        System.out.println(THREAD_LOCAL.get());
    }
}

类似于这种,当然,我这个只是作为一个演示的例子
点进去set方法中查看,看看如何存储数据的
ThreadLoc中set方法
可视化的样子大概就是:
ThreadLoca存储
我们知道,弱引用是只要进行GC,就会被回收
**那么,为什么key需要使用WeakReference呢?**我认为是:当threadLocal=null之后,也就是没有强引用了,那么这个key实际上就没有用了,那么使用弱引用则能达到GC回收这个key,这样,在你下次进行set/get/remove的时候,实际上ThreadLocal就会把这些key对应的Entry做清除处理
set方法:
set清理
三个方法可以自己看一下
get方法:
get清理
get如果都没有找到的话,就会初始化一个变量,然后就会走到set方法
remove方法:
remove清理

为什么ThreadLocal的Entry的value为强引用?

既然key为弱引用可以利于回收,那么value为什么不用弱引用呢?
再次强调:弱引用会在GC时被回收,不论内存够不够
示例:

import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class ThreadLocalTest {

    private static ThreadLocal<String> threadLocal = ThreadLocal.withInitial(() -> new String("888"));

    public static void main(String[] args) throws InterruptedException {
        weakReferenceValue();
        internetTest();
    }


    private static void weakReferenceValue() throws InterruptedException {
        Map<WeakReference<ThreadLocal<String>>, WeakReference<String>> map = new HashMap<>();
        WeakReference<String> value = new WeakReference<>(new String("777"));
        WeakReference<ThreadLocal<String>> key = new WeakReference<>(ThreadLocal.withInitial(() -> new String("888")));
        map.put(key, value);
        System.out.println(map.get(key).get());
        System.out.println(key.get().get());
        TimeUnit.SECONDS.sleep(1);
        System.gc();
        System.out.println(key.get().get());
        System.out.println(map.get(key).get());

        // 所以,实际上就是,ThreadLocalMap的Entry中key之所以为弱引用,是因为当ThreadLocal变量没有强引用的时候,能自动回收
        // 并且,重新进行set等操作,会清除这个Entry,而value是强引用则是因为如果为弱引用,那么但凡有一次GC,这个值就没了
    }
}

结果:
强引用示例
可以见到,弱引用,GC之后,就会为null,我们注释掉key.get().get()那一行,再次运行
强引用示例2
看,出现了null,假设你去拿数据之前,来了一波GC,那么,恭喜:空指针伺候

ThreadLocal的内存泄漏?

其实看了第一个问题的解答,我们就知道,内存泄漏的概率还是比较低的

  1. ThreadLocal的强引用一直在,线程复用
  2. ThreadLocal的强引用不在,线程复用,未调用get/set/remove方法

所以,我认为概率比较低,而且大部分是我们使用ThreadLocal后,未做相应的资源清理
所以,建议用了之后,remove掉(注意,不要直接将ThreadLocal的变量置为null,因为一般ThreadLocal都为static,因为它一般只需要处理一次)

ThreadLocal里面为什么不直接内置一个Map,然后key为thread呢?

个人理解:因为这样的话,能存的变量就少了,即使能存多,也是需要 手段的,比如使用一个对象来存储数据。相较于当前的设计,差太多了。

总结

  1. ThreadLocal中Entry的key为什么是弱引用?
    因为利于ThreadLocal的内部回收,相较于强引用,更能防止内存泄漏
  2. ThreadLocal中Entry的value为什么是强引用?
    因为如果是弱引用什么的,如果取值之前发生了GC,那么拿到的值会为null
  3. ThreadLocal的内存泄漏
    出现的最多的,一般是:线程复用,ThreadLocal变量的强引用还在
  4. ThreadLocal里面为什么不直接内置一个Map,然后key为thread呢?
    不够优雅吧,存多个数据需要更多的手段

若有错误之处,烦请指出,不胜感激

  • 23
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值