被面试官问懵了,ThreadLocal的key为什么设置成弱引用?

前言

想起来去年九月夜郎自大,面过一次蚂蚁金服,一面面试官上来一串ThreadLocal的连环炮直接把我干懵了……虽然现在回想起来问得都挺简单,不过还是来做一发总结,希望看到这篇文章的小伙伴以后就不要栽坑里了。

面试连环炮

先来体验一下关于ThreadLocal的连环炮:

  1. ThreadLocal是什么?项目中用到过吗?
  2. ThreadLocal的结构是怎么样的?
  3. 使用ThreadLocal需要注意哪些问题?
  4. 为什么key要设置成弱引用呢?
  5. 那为什么value不设置成弱引用呢?ThreadLocalMap不是持有对这个value的强引用,它还会被回收吗?
  6. 为什么会出现内存泄漏?你是怎么发现内存泄漏的?
  7. 怎么避免出现脏数据问题?

其实说实话,这几个问题都不难,稍微对ThreadLocal有点了解的都能答上来,关键在于第4个问题和第5个问题

key为什么要设置成弱引用?

先来看看ThreadLocalMap对key和value的构造:

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;
    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

在set方法中,会将key和value以entry对象进行存储,key就是我们的Threadlocal引用,value就是我们要设置的值。

这里我们观察Entry这个类就会发现,它继承了WeakReference类,并且在构造方法中,将key设置成了弱引用,而value则是强引用。

为什么要这样做?

要知道,ThreadlocalMap是和线程绑定在一起的,如果这样线程没有被销毁,而我们又已经不会再某个threadlocal引用,那么key-value的键值对就会一直在map中存在,这对于程序来说,就出现了内存泄漏

为了避免这种情况,只要将key设置为弱引用,那么当发生GC的时候,就会自动将弱引用给清理掉,也就是说:假如某个用户A执行方法时产生了一份threadlocalA,然后在很长一段时间都用不到threadlocalA时,作为弱引用,它会在下次垃圾回收时被清理掉。

而且ThreadLocalMap在内部的set,get和扩容时都会清理掉泄漏的Entry,内存泄漏完全没必要过于担心。

value为什么不设置成弱引用

回答完上面的问题,面试官紧接着就会问你:

“那照你的说法,value也应该设置成弱引用才对呀?不然不是一样会发生内存泄漏?”

对于这个问题,如果没有仔细思考,猝不及防下可能就会被问懵,对啊,既然key设置成弱引用的好处这么明显,那为什么value不也设置成弱引用呢?

我们来看下面一段代码:

public static void main(String[] args) throws Exception {
    Map<WeakReference<Integer>, WeakReference<Integer>> map = new HashMap<>(8);
    WeakReference<Integer> key = new WeakReference<>(666);
    WeakReference<Integer> value = new WeakReference<>(777);
    map.put(key,value);
    System.out.println("put success");
    Thread.sleep(1000);
    System.gc();
    System.out.println("get " + map.get(key).get());
}

猜一下结果会怎样?

完蛋了,我在方法里执行我的代码时居然会出现空指针??

可能有些小伙伴不太懂,明明有value这个强引用啊?

可惜value这个强引用是指向new WeakReference<>(777)的,也就是说,777这个才是我们真正存进去的对象,这时候它已经不存在任何强引用了,所以理所当然的会被清理掉啦。

所以为什么value为什么不设置成弱引用的原因不用我说大家也应该清楚了。

最后留个小彩蛋,上面这段代码,如果换成下面:

public static void main(String[] args) throws Exception {
    Map<WeakReference<Integer>, WeakReference<Integer>> map = new HashMap<>(8);
    // 注意这里~
    WeakReference<Integer> key = new WeakReference<>(1);
    WeakReference<Integer> value = new WeakReference<>(2);
    map.put(key,value);
    System.out.println("put success");
    Thread.sleep(1000);
    System.gc();
    System.out.println("get " + map.get(key).get());
}

猜一下结果会怎样?

评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值