ThreadLocal与WeakReference

ThreadLocal是面试中比较容易碰上的问题,一般会要求讲解它的实现原理以及存在的问题。最近在美团的面试中聊到这个问题,虽然去年有看过并收藏了关于ThreadLocal的一些文章但目前几乎忘了干净(还是实践以及理解的不够没有形成记忆 …),所以趁着这个机会好好的总结记录一下。

ThreadLocal的实现机制不复杂,它将自身实例作为key,和需要保存的value一起存入到当前线程的一个map当中,代码可以简单的描写为(当然实际的代码并不是这样):

`Thread.currentThread().threadLocals.put(this, value);// threadLocal.set(T)

Thread.currentThread().threadLocals.get(this);// threadLocal.get()`

ThreadLocal实现中特别值得注意的是1点:每个线程中都保存着一个threadLocals实例,该实例是Map接口的实现 – ThreadLocalMap。而这个类实现的特殊地方在于,ThreadLocalMap中的Entry中的key类型是WeakReference而非ThreadLocal。为什么ThreadLocalMap中需要使用WeakReference作为key类型,那么首先需要理解WeakReference的意义。

WeakReference是Java语言规范中为了区别直接的对象引用(程序中通过构造函数声明出来的对象引用)而定义的另外一种引用关系。WeakReference标志性的特点是:reference实例不会影响到被应用对象的GC回收行为(即只要对象被除WeakReference对象之外所有的对象解除引用后,该对象便可以被GC回收),只不过在被对象回收之后,reference实例想获得被应用的对象时程序会返回null。

理解了WeakReference之后,ThreadLocalMap使用它的目的也相对清晰了:当threadLocal实例可以被GC回收时,系统可以检测到该threadLocal对应的Entry是否已经过期(根据reference.get() == null来判断,如果为true则表示过期,程序内部称为stale slots)来自动做一些清除工作,否则如果不清除的话容易产生内存无法释放的问题:value对应的对象即使不再使用,但由于被threadLocalMap所引用导致无法被GC回收。实际代码中,ThreadLocalMap会在set,get以及resize等方法中对stale slots做自动删除(set以及get不保证所有过期slots会在操作中会被删除,而resize则会删除threadLocalMap中所有的过期slots)。当然将threadLocal对象设置为null并不能完全避免内存泄露对象,最安全的办法仍然是调用ThreadLocal的remove方法,来彻底避免可能的内存泄露。

你可能感兴趣的:(ThreadLocal与WeakReference)