threadlocal的实现说明
目录
1.说明
2.弱引用的说明
3.最佳实践
4.方法说明
1.说明
在每个Thread中包含一个threadlocalmap对象,ThreadLocalMap是TheadLocal的静态内部类
threadLocalMap内部是一个entry数组
每个entry的key是TheadLoca实例,key是一个弱引用
每个entry的value是设置的内容,是强引用
如果在方法中new 一个threadlocal实例时,内存占用如下:
+------------------+ +-------------------+ +-----------------------+
| 方法栈帧 | | 堆内存 | | 线程的 ThreadLocalMap |
| | | | | |
| threadLocal 引用 ──────> ThreadLocal实例 | | +-------------------+ |
+------------------+ +-------------------+ | | Entry: | || | Key: 弱引用ThreadLocal || | Value:强引用"value" | || +-------------------+ |+-----------------------+
当方法结束后,方法栈帧弹出,这时ThreadLocal对象的引用消失,ThreadLocalMap中的Key(ThreadLocal对象)是弱引用(WeakReference)。
当外部对ThreadLocal的强引用消失后,GC回收时发现无强引用指定,
仅有 Entry 的弱引用指向
→ 立即回收 Key 对象,并将 Entry 的 Key 置为 null(弱引用自动被清除)。
虽然 Key 被回收,但 Entry 中的 value 仍是强引用,此时会形成:
Entry { key=null, value="SomeValue" } // Value 无法被访问,存在内存泄漏风险
2.弱引用的说明
弱引用特性:当 JVM 进行垃圾回收时,如果 Key 仅被弱引用指向(没有强引用),无论内存是否充足,都会回收该 Key 对象。
为什么设计为弱引用?
主要目的:防止开发者忘记调用 remove() 时,ThreadLocal 对象本身无法被回收。
如果 Key 是强引用:即使开发者不再使用 ThreadLocal,由于线程的 ThreadLocalMap 持有强引用,ThreadLocal 对象和 Value 会永远无法回收。
弱引用设计是一种兜底机制,至少能回收 Key,但需配合 remove() 彻底清理。
3.最佳实践
①使用static + final 定义threadlocal实例
// 推荐:static + final 修饰
private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>();
static 确保全局唯一实例,避免重复创建,不是对象属性,而是类的属性,一直持有引用,除非类被卸载,final 防止意外修改引用
②使用完成后执行remove处理
try {
CONTEXT_HOLDER.set(value);
} finally {
CONTEXT_HOLDER.remove(); // 必须清理
}
remove处理会清楚key和value,确保可以被回收
4.方法说明
get方法:
public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();}
- 获取当前线程
- 根据线程获取线程的threadlocalmap变量
- 如果为空,代表首次赋值,会创建一个threadlocalmap实例,key设置为threadlocal实例,并将值保存为null
- 如果不为空,则根据threadlocal实例获取对应的内容
set方法:
public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);}
- 获取当前线程
- 获取线程的threadlocalmap变量
- 如果不为空,key设置为threadlocal实例,并将值保存为参数中传递的内容
- 如果为空,会创建一个threadlocalmap实例,key设置为threadlocal实例,并将值保存为参数中传递的内容
remove方法:
public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this);}
会清除key及value,并且无效Entry被清理