ThreadLocal内存泄漏及弱引用设计原因

  ThreadLocal用来干什么的?

   在ThreadLocal类中注释如下:为该类提供线程局部变量。这些变量不同于它们的普通对应物,每个访问它们的线程(通过其{@code get}或{@code set}方法)都有自己独立初始化的变量副本。{@code ThreadLocal}实例通常是类中的私有静态字段,这些类希望将状态与线程关联(例如,用户ID或事务ID)。

  那么ThreadLocal的作用就是将变量封闭在该线程中,确保其是线程安全的。现在我们想给每一个线程分配一个特殊的连接。代码如下:

public class MyThread {
    private static final ThreadLocal<String> threadId = new ThreadLocal<>();

    public static void main(String[] args) {
        new Thread(()->{
            threadId.set("connection_1");
            System.out.println(threadId.get());
            threadId.remove();
        }).start();

        new Thread(()->{
            threadId.set("connection_2");
            System.out.println(threadId.get());
            threadId.remove();
        }).start();
    }
}

 此时我们就可以在线程里安全的使用分配给线程的连接。

ThreadLocal原理

  ThreadLocal之所以能将变量进行线程内封闭,主要是使用到了一个ThreadLocalMap的类。它是ThreadLocal中一个静态内部类,它就是一个Map结构,底层放的元素是使用ThreadLocal作为key,变量作为value的Entry。

  Map的创建时机为第一次调用set()函数时,会判断getMap()是否为空,如果为空则会创建ThreadLocalMap并与线程绑定。

ThreadLocal内存泄漏

  在使用ThreadLocal时,需要强调的一点是使用完毕后记得调用remove()方法进行手动的删除。否则会导致内存泄漏。那么为啥垃圾收集器收拾不掉这个小辣鸡呢?请看下面这张图

  首先介绍一下弱引用:弱引用用来描述那些非必须对象, 只被弱引用关联的对象只能生存到下次垃圾收集发生。当垃圾收集器开始工作时,无论当前内存是否足够,都会回收掉它。下图是Entry类代码

static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }

    那么假设我们此时已经使用完了ThreadLocal,那么ThreadLocal会被回收掉,但注意到Thread到Entry是存在着一条生命周期跟随线程的引用链的。那么value对象就会一直存在于内存中。

  说明白了内存泄漏,那么为什么ThreadLocal要被设计为弱引用呢?很多文章说这是为了防止ThreadLocal的内存泄漏就止步于此。其实我们再看一下源码就可以发现,在get()方法时,如果ThreadLocal为null,那么肯定找不到对应的Entry,此时会一路调用到setInitialValue()函数,代码如下:

private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            map.set(this, value);
        } else {
            createMap(t, value);
        }
        if (this instanceof TerminatingThreadLocal) {
            TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
        }
        return value;
    }

  initialValue()会直接返回一个null,那么此时我们会把Entry中对value的引用置为空。那么这个value对象的引用链就消失了。因此,我们可以把弱引用的设计看作是设计者对没有手动调用remove()方法进行删除的一种保护手段。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值