转载

ThreadLocal详解

概述

做Java开发的对ThreadLocal的肯定不会陌生, 它的作用是提供线程内的局部变量,这种变量在多线程环境下访问时能够保证各个线程里变量的独立性。也是Java面试的必备考点。

TheadLocal典型用法

我们知道SimpleDateFormat是非线程安全,在多线程使用下可能会有问题。

针对这个问题,可以有几种解决方案:

  1. 每次要使用 SimpleDateFormat 时都创建一个局部的 SimpleDateFormat对象。局部变量,自然就不存在线程安全的问题了。但如果需要频繁进行调用的话,每次都要创建新的对象,开销太大。

  2. 就是对 SimpleDateFormat 进行加锁,这样可以确保同一时间只有一个线程可以持有锁,进而解决线程安全的问题。但是这种方法在多线程竞争激烈的时候会带来效率问题

  3. 就是使用 ThreadLocal。 ThreadLocal 可以确保每个线程都可以得到单独的一个 SimpleDateFormat 的对象,那么自然也就不存在竞争问题了。

ThreadLocal详解

TheadLocal原理分析

TheadLocal的内部结构示意图如下:

ThreadLocal详解

从结构示意图真正存储值得地方是放在了线程对象中,Thread对象中有个ThreadLocalMap对象,该对象中的key是ThreadLocal对象,value是用户真正存储的值。

以ThreadLocal 中的set为例:

ThreadLocal详解

ThreadLocal详解

在ThreadLocal的 set方法中,首先获取了当前线程,然后获取线程中的ThreadLocalMap,如果map不会null,则直接put到该map中,其中key就是threadLocal对象,value即用户set的值。

ThreadLocal 如何避免内存泄漏的呢

如果ThreadLocalMap中的key和value都是普通对象,可能会存在什么问题呢?

当用户如果忘记remove对象时并且该线程一直存在时,就会就会造成线程对key、value的引用一直存在造成内存泄漏。

为了避免内存泄漏,ThreadLocalMap中的key采用了WeakReference。

ThreadLocal详解

这样保证了即使用户忘记调用remove,也能保证key被回收调。

那么value怎么办呢,因为value不是 WeakReference,那么JDK是如何保证value不存在内存泄漏呢?

其实在 ThreadLocalMap有个expungeStaleEntry, 这个方法在ThreadLocalMap get、set、remove、rehash等方法都会调用到。 看下面标红的两处代码,第一处是将remove的entry赋空,第二次处是找到已经被GC的ThreadLocal,然后会清理掉table数组对entry的引用。这样entry在后续的GC中就会被回收。

ThreadLocal详解

ThreadLocal最佳实践

  1. 在使用完成之后,要显示的调用remove释放。

  2. 官方推荐使用private static修饰。其中private比较容易理解;加上static之后,就不会频繁的创建弱引用,这样就能减少弱引用对GC的影响。

原文  http://mp.weixin.qq.com/s?__biz=MzUzODQ0MDY2Nw==&mid=2247483994&idx=1&sn=76a9552577a4fbea59f6dbdbf3c9044f
正文到此结束
Loading...