ThreadLocal的使用和注意事项

一、前言

对ThreadLocal不熟悉的同学,可以先参考我的另外一篇文章浅谈ThreadLocal

在讨论内存泄漏之前,需要明白java中的四种引用,同样可以移步到java中的四种引用

什么是内存泄露?

大白话讲,就是我自己创建的对象,在一系列操作后,我访问不到该对象了,我认为它已经被回收掉了,但该对象却一直存在与内存中。
二、示例

先给出一个简单例子,用来说明引用与对象的指向关系
 

package com.yang.testThreadLocal;
 
public class Main {
 
    private static ThreadLocal<Integer> tl = new ThreadLocal<>();
 
    public static void main(String[] args) {
 
        tl.set(1);
 
        Thread t = new Thread(() -> {
            tl.set(2);
            System.out.println("子线程:" + tl.get());
        });
        t.start();
 
        System.out.println("主线程:" + tl.get());
 
    }
}

指向关系如下所示

在ThreadLocalMap中,维护一个Entry类型的数组,Entry是一个(k,v)结构,可以把ThreadLocalMap理解为一个定制化的HashMap。不过Entry的key是对ThreadLocal的一个弱引用,在执行tl=null后,1号线断开,则该ThreadLocal会在下一次GC到来的时候,被回收掉。

为什么这里的key保持着对ThreadLocal的一个弱引用呢?保持强引用行不行?

假设这里的key保持对ThreadLocal的强引用,则当我的程序用不到该ThreadLocal时,我手动执行了tl=null,此时1号线断开,而这里的5号线是实线,5号线没有断开,因此ThreadLocal对象无法被回收掉,一直存在于内存中,造成内存泄露。

看来,这里的弱引用,能够保证用不到的ThreadLocal被回收掉。

弱引用就能完全防止内存泄露了吗?

由上面的分析,弱引用能够防止释放不掉ThreadLocal引起的内存泄露。但是,却不能防止释放不掉Integer引起的内存泄露。首先,执行tl=null,则1号线断开,GC到来时,5号线断开,此时ThreadLocal被回收掉了,这个key被置为了null,可是这个key对应的value强引用着Integer对象,该Integer无法在用户代码中访问到了,但却依然存在于内存中,造成内存泄露。

既然依然存在着内存泄露,那么JDK团队是怎么解决的呢?

其实,ThreadLocal中的get()、set()方法,不是单纯地去做获取、设置的操作。在它们的方法内部,依然会遍历该Entry数组,删除所有key为null的Entry,并将相关的value置为null,从而够解决因释放不掉value而引起的内存泄露。

有这些get()、set()方法,就能完全地防止内存泄漏吗?

但我们手动将tl置为null后,就已经没法调用这些get()、set()方法了。所以,预防内存泄露的最佳实践是,在使用完ThreadLocal后,先调用tl.remove(),再调用tl=null。tl.remove()能够使得ThreadLocalMap删除该ThreadLocal所在的Entry,以及将value置为null,tl=null使得ThreadLocal对象真正地被回收掉。

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值