概念
- ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。
作用
- ThreadLocal允许我们将每个线程 ID 与相应对象的值相关联。 它允许我们为不同的线程存储不同的对象,并维护哪个对象对应于哪个线程。它有 set 和 get 方法,这些方法为使用它的每个线程维护一个单独的 value 副本。get() 方法总是返回从当前正在执行的线程传递给 set()的最新值
应用
- JDBC的connection对象,为了避免connection对象作为方法参数多次传递
- spring的事务
缺点
- 降低了代码的可重用性
- 类之间有隐藏的参数耦合性,使用时要格外的小心
踩坑
- 线程池中使用ThreadLocal造成内存泄漏
- 主要原因是因为ThreadLocal的释放,并不能直接让其对应的value被释放。
- 在线程池中使用threadLocal时,thread会被反复的使用,因此theadLocalMap也不会被释放
- 如果没有set, get, remove方法的调用,threadLocalMap中的Value,即使在theadLocal被设置为null后,仍然不能被释放
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tqc1OKis-1580718753970)(evernotecid://E654E5B8-6FF0-43A7-9FDE-3146227D8C68/appyinxiangcom/14842348/ENResource/p2858)]
应对办法:
在使用完自定义的threadLocal变量之后执行remove方法
ExecutorService es;
ThreadLocal tl;
es.execute(()->{
//ThreadLocal增加变量
tl.set(obj);
try {
// 省略业务逻辑代码
}finally {
//手动清理ThreadLocal
tl.remove();
}
});
例子
给每个线程分配一个单独的id
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadId {
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer>; threadId =
new ThreadLocal<Integer>() {
@Override protected Integer initialValue() {
return nextId.getAndIncrement();
}
};
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
}
常见面试题
- ThreadLocal的应用场景? @见笔记
- ThreadLocal内存泄漏的原因 @见笔记
参考
- 极客时间 - 王宝令 - Java并发编程实战 -30 | 线程本地存储模式:没有共享,就没有伤害
- 《Java并发编程实践》
- ThreadLocal 定义,以及是否可能引起的内存泄露(threadlocalMap的Key是弱引用,用线程池有可能泄露)