18.3.7 ThreadLocal原理与内存泄漏问题
1. ThreadLocal基础概念
1.1 ThreadLocal简介
public class ThreadLocalBasics {
/*
* ThreadLocal作用:
* 为每个线程提供独立的变量副本,线程间互不影响
*
* 核心特点:
* 1. 线程隔离:每个线程有自己的变量副本
* 2. 无需同步:避免线程安全问题
* 3. 生命周期:与线程生命周期绑定
*/
private static ThreadLocal<String> threadLocalValue = new ThreadLocal<>();
private static ThreadLocal<Integer> threadLocalCounter = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0; // 设置初始值
}
};
public void demonstrateBasicUsage() {
// 创建多个线程,每个线程设置不同的值
for (int i = 1; i <= 3; i++) {
final int threadId = i;
Thread thread = new Thread(() -> {
// 每个线程设置自己的值
threadLocalValue.set("线程" + threadId + "的值");
threadLocalCounter.set(threadId * 10);
// 模拟一些操作
doSomeWork();
// 读取值
System.out.println(Thread.currentThread().getName() +
" - ThreadLocal值: " + threadLocalValue.get() +
", 计数器: " + threadLocalCounter.get());
// 清理资源
threadLocalValue.remove();
threadLocalCounter.remove();
}, "Worker-" + threadId);
thread.start();
}
}
private void doSomeWork() {
try {
Thread.sleep(1000);
// 在方法中也能访问到ThreadLocal的值
String value = threadLocalValue.get();
Integer counter = threadLocalCounter.get();
System.out.println(Thread.currentThread().getName() +
" 工作中 - 值: " + value + ", 计数器: " + counter);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
2. ThreadLocal实现原理
2.1 内部数据结构
public class ThreadLocalInternals {
/*
* ThreadLocal实现原理:
*
* 1. 每个Thread对象都有一个ThreadLocalMap字段
* 2. ThreadLocalMap是ThreadLocal的内部类
* 3. ThreadLocalMap使用ThreadLocal对象作为key
* 4. 实际数据存储在当前线程的ThreadLocalMap中
*
* 关键数据结构:
* Thread.threadLocals -> ThreadLocalMap
* ThreadLocalMap.table -> Entry[]
* Entry extends WeakReference<ThreadLocal<?>>
*/
public void demonstrateInternalStructure() {
ThreadLocal<String> tl1 = new ThreadLocal<>();
ThreadLocal<Integer> tl2 = new ThreadLocal<>();
Thread thread = new Thread(() -> {
// 设置多个ThreadLocal值
tl1.set("字符串值");
tl2.set(42);
System.out.println("ThreadLocal1: " + tl1.get());
System.out.println("ThreadLocal2: " + tl2.get());
// 清理
tl1.remove();
tl2.remove();
}, "InternalThread");
thread.start();
}
}
2.2 get/set实现原理
public class ThreadLocalImplementation {
/*
* ThreadLocal核心方法实现原理:
*
* set(T value):
* 1. 获取当前线程
* 2. 获取线程的ThreadLocalMap
* 3. 如果map存在,设置值;否则创建map
*
* get():
* 1. 获取当前线程
* 2. 获取线程的ThreadLocalMap
* 3. 从map中根据ThreadLocal对象获取值
* 4. 如果没有值,返回初始值
*/
// 模拟ThreadLocal实现
static class CustomThreadLocal<T> {
private final Map<Thread, T> threadMap = new ConcurrentHashMap<>();
public void set(T value) {
Thread currentThread = Thread.currentThread();
threadMap.put(currentThread, value);
System.out.println("CustomThreadLocal.set() - 线程: " + currentThread.getName() +
", 值: " + value);
}
public T get() {
Thread currentThread = Thread.currentThread();
T value = threadMap.get(currentThread);
System.out.println("CustomThreadLocal.get() - 线程: " + currentThread.getName() +
", 值: " + value);
return value;
}
public void remove() {
Thread currentThread = Thread.currentThread();
threadMap.remove(currentThread);
System.out.println("CustomThreadLocal.remove() - 线程: " + currentThread.getName());
}
}
}
3. 内存泄漏问题分析
3.1 内存泄漏产生原因
public class ThreadLocalMemoryLeak {
/*
* ThreadLocal内存泄漏原因:
*
* 1. ThreadLocalMap的Entry继承WeakReference<ThreadLocal<?>>
* 2. key是弱引用,但value是强引用
* 3. 当ThreadLocal对象被GC回收后,key变为null
* 4. 但value仍然被Entry强引用,无法回收
* 5. 如果线程长期存活,会导致内存泄漏
*
* 引用链:
* Thread -> ThreadLocalMap -> Entry -> value (强引用)
*/
public void demonstrateMemoryLeak() {
// 创建长期存活的线程
Thread longLivedThread = new Thread(() -> {
// 创建大量ThreadLocal对象
for (int i = 0; i < 100; i++) {
ThreadLocal<byte[]> threadLocal = new ThreadLocal<>();
// 设置大对象
byte[] largeObject = new byte[1024 * 100]; // 100KB
threadLocal.set(largeObject);
// ThreadLocal对象超出作用域,可能被GC
// 但value仍然被强引用,造成内存泄漏
}
System.out.pri
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
Java面试圣经 文章被收录于专栏
Java面试圣经,带你练透java圣经

查看13道真题和解析