本篇深度剖析下ThreadLocal。主要从下面几个方面:
- 为什么要用ThreadLocal
- ThreadLocal 内部实现原理(原理剖析)
- ThreadLocal为什么会内存溢出可能性.
为什么要用ThreadLocal
通常情况下,我们在方法中需要使用方法外的变量,有下面两种方式:
- 命名为静态变量
- 弊端:会有线程不安全的问题
- 作为方法传参
- 弊端,改动比较大
所以SUN公司就提出了ThreadLocal。存储变量的时候, ThreadLocal会获取当前线程,并将需要存储的变量存储关联到Thread.threadLocals(实际类型是ThreadLocalMap) 引用中。获取的时候,ThreadLocal会获取当前线程中的threadLocals, 从其获取对应的数据。这样子就避免了线程安全问题,以及方法传参复杂性过高问题。
适用场景
从上面的解释来看,ThreadLocal的使用场景,就是在方法内使用方法外的变量,并且不希望对整个项目接口改动,就可以使用ThreadLocal.
ThreadLocal 内部实现原理
概览: ThreadLocal 实现原理简单讲,就是根据本地的
set
1 | public void set(T value) { |
ThreadLocalMap
主要功能是,根据传入的Threadlocal对象做key,创建一个Entry对象(其中Entry key为弱引用,value为强引用),存放到数组中。
从上面也可以看出ThreadLoacl 不做实际的存储,是做查找的key
类方法图

set
1 | private void set(ThreadLocal<?> key, Object value) { |
getEntry
获取entry
1 | private Entry getEntry(ThreadLocal<?> key) { |
remove 移除一个entry
1 | /** |
get && remove
get其实就是ThreadLocalMap.getEntry的代理。
remove其实就是ThreadLocalMap.remove的代理。
1 | public T get() { |
ThreadLocal为什么会内存溢出可能性.
ThreadLocalMap 中的Entry数据结构
1 | static class Entry extends WeakReference<ThreadLocal<?>> { |
注意到Entry的key 是WeakReference
类型了么,这个是弱引用。
如果线程一直存在,但是Entry中的key为null,但是value是强引用,其实就是会存在某些value一直没有被gc回收,所以就是会存在内存溢出。
解决办法
使用完之后调用下remove即可。 ThreadLocalMap.getEntry和setEntry的都是会调用expungeStaleEntry,expungeStaleEntry如果检测到key==null,也是会将value置为null的