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很多同学都搞不懂是什么东西,可以用来干嘛。但面试时却又经常问到,所以这次我和大家一起学...
    liangzzz阅读 12,506评论 14 228
  • Android Handler机制系列文章整体内容如下: Android Handler机制1之ThreadAnd...
    隔壁老李头阅读 7,684评论 4 30
  • ThreadLocal,线程变量,是一个以ThreadLocal对象为键,任意对象为值 的存储 结构。该结构附着于...
    Justlearn阅读 418评论 0 2
  • 这篇文章是我之前翻阅了不少的书籍以及从网络上收集的一些资料的整理,因此不免有一些不准确的地方,同时不同JDK版本的...
    高广超阅读 15,738评论 3 83
  • 也不知道是何时开始,默默出现月经综合症。每个月都会有那么几天会情绪低落,易暴躁,易发火。 从早晨起来就深深的在纠结...
    文文的花期阅读 311评论 0 1