Java高频面试之并发编程-09
hello啊,各位观众姥爷们!!!本baby今天来报道了!哈哈哈哈哈嗝🐶
面试官:详细说说ThreadLocal
ThreadLocal 是 Java 中用于实现线程本地变量的工具类,主要解决多线程环境下共享变量的线程安全问题。以下是其核心要点:
1. 核心作用
- 线程隔离:每个线程拥有独立的变量副本,避免多线程竞争。
- 无锁优化:通过空间换时间,消除同步开销。
2. 实现原理
-
ThreadLocalMap:
- 每个线程(
Thread
类)内部维护一个ThreadLocalMap
,以ThreadLocal
实例为键,存储线程本地变量。 - 结构类似哈希表,采用开放地址法(线性探测)解决哈希冲突。
- 每个线程(
-
关键操作:
- set(T value):将值存入当前线程的
ThreadLocalMap
。 - get():从当前线程的
ThreadLocalMap
中获取值。 - remove():清除当前线程的
ThreadLocalMap
中的值。
- set(T value):将值存入当前线程的
3. 内存泄漏问题
-
原因:
ThreadLocalMap
的键(ThreadLocal
实例)是弱引用,值(变量副本)是强引用。- 若
ThreadLocal
实例被回收,但线程未终止,会导致值无法被回收(键为 null,但值仍存在)。
-
解决方案:
- 使用后主动调用
remove()
清理条目。 - 避免长时间持有线程(如线程池中线程复用)。
- 使用后主动调用
4. 应用场景
-
数据库连接管理:每个线程维护独立连接。
private static ThreadLocal<Connection> connectionHolder = ThreadLocal.withInitial(() -> DriverManager.getConnection(DB_URL));
-
会话管理:保存用户请求上下文(如 Spring 的
RequestContextHolder
)。 -
日期格式化:避免
SimpleDateFormat
非线程安全。private static ThreadLocal<SimpleDateFormat> dateFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
5. 使用注意事项
- 线程池中的清理:线程复用可能导致残留数据,务必在任务结束时调用
remove()
。 - 避免全局滥用:过度使用会增加内存压力。
- 继承问题:默认子线程无法访问父线程变量,需用
InheritableThreadLocal
。
6. 示例代码
public class ThreadLocalDemo {private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(2);// 任务 1:设置值为 100executor.submit(() -> {threadLocal.set(100);try {System.out.println("Thread 1: " + threadLocal.get()); // 输出 100} finally {threadLocal.remove(); // 清理}});// 任务 2:未设置值,获取为 nullexecutor.submit(() -> {System.out.println("Thread 2: " + threadLocal.get()); // 输出 null});executor.shutdown();}
}
7. 与 InheritableThreadLocal 的区别
- ThreadLocal:子线程无法继承父线程变量。
- InheritableThreadLocal:子线程创建时自动复制父线程变量。
private static InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();public static void main(String[] args) {inheritableThreadLocal.set("Parent Value");new Thread(() -> {System.out.println(inheritableThreadLocal.get()); // 输出 "Parent Value"}).start(); }
总结
- 优点:简化线程安全设计,提升性能。
- 缺点:需谨慎处理内存泄漏和线程池清理。
- 适用场景:线程封闭、上下文传递、独立资源管理。