ThreadLocal原理分析
ThreadLocal是一个用于实现线程安全的“利器”,主要是通过空间换时间的方法去实现。每一个线程内部都有一个ThreadLocalMap类型的变量,名为threadLocals,这是一个key-value类型的哈希表,键为ThreadLocal实例,要存储的线程局部变量为值
ThreadLocal是怎么用的?
public class BasicExample {
// 创建一个ThreadLocal变量,并为其提供初始值
private static ThreadLocal
<Integer> threadLocalCounter = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0; // 返回你想要的初始值
}
};
public static void main(String[] args) {
Runnable task = () -> {
// 每个线程都会独立地操作自己的counter副本
int currentValue = threadLocalCounter.get(); // 获取本线程的值
threadLocalCounter.set(currentValue + 1); // 修改本线程的值
System.out.println(Thread.currentThread().getName() + " 的计数器: " + threadLocalCounter.get());
// !!!重要:使用完毕后及时清理,避免内存泄漏
threadLocalCounter.remove()[1](@ref);
};
new Thread(task, "线程A").start();
new Thread(task, "线程B").start();
}
}
ThreadLocal的原理
// ThreadLocal 的 set 方法源码概要
public void set(T value) {
Thread t = Thread.currentThread(); // 1. 获取当前线程
ThreadLocalMap map = getMap(t); // 2. 获取线程的 ThreadLocalMap
if (map != null) {
map.set(this, value); // 3. 以当前 ThreadLocal 为键,存储值
} else {
createMap(t, value); // 如果 Map 不存在,则创建一个
}
}
// ThreadLocal 的 get 方法源码概要
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this); // 以当前 ThreadLocal 为键,获取值
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue(); // 如果 Map 不存在或找不到值,则初始化
}
private T setInitialValue() {
// 1. 获取初始值,默认由 initialValue() 方法提供,通常为 null
T value = initialValue();
// 2. 获取当前线程对象
Thread t = Thread.currentThread();
// 3. 获取当前线程的 ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
// 4. 如果 Map 已存在,将当前 ThreadLocal 实例与初始值关联
map.set(this, value);
} else {
// 5. 如果 Map 不存在,则为当前线程创建一个新的 ThreadLocalMap
createMap(t, value);
}
// 6. 返回初始值
return value;
}
ThreadLocal的内存泄漏是怎么产生的?
根源: ThreadLocalMap中的键(ThreadLocal实例)是弱引用,而值是强引用。如果 ThreadLocal实例被垃圾回收(比如外部将其设为 null),会导致 Map 中的键为 null,但值仍然被强引用且无法再访问到,从而造成内存泄漏
解决方案。每次使用完 ThreadLocal后,在代码块中调用其 remove()方法,显式移除数据。