概述
源码介绍:
* This class provides thread-local variables. These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* {@code get} or {@code set} method) has its own, independently initialized
* copy of the variable. {@code ThreadLocal} instances are typically private
* static fields in classes that wish to associate state with a thread (e.g.,
* a user ID or Transaction ID).
*
大体上是这个意思:
这个类提供了线程局部变量。这些变量的不同在于每一个线程都有自己的、独立初始化的变量副本(通过它的get和set方法)。在希望将状态与线程关联的类中,ThreadLocal实例通常是私有的、静态的(例如,用户ID或事务ID)。
相信你绝对在网上看见过这样的描述:
ThreadLocal为解决多线程程序的并发问题提供了一种新的思路;ThreadLocal的目的是为了解决多线程访问资源时的共享问题。
看完源码介绍,你也应该知道上面这种解释到底是对还是不对了吧~
简单总结ThreadLocal的作用:
提供线程内部的局部变量,在线程生命周期内有效。(再次强调,不是为了线程之间共享数据~)
常用API
public void set(T value) 设置当前线程的值
public T get() 拿出当前线程的值
public void remove() 删除当前线程的值
protected T initialValue()初始化当前线程的值(注意这是一个protected修饰的方法。JDK告诉我们可以自己实现)
实现方式
贴出一点源码你就知道是怎样实现的了
- set方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
- getMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
- threadLocals是什么?
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
- threadLocals在哪里初始化的? 看下set方法的createMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
- ThreadLocalMap的源码大家可以去看下
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
设计思路总结:
每个Thread维护一个ThreadLocalMap映射表,这个表的key是当前线程本身,value是真正需要存储的对象。
问题 : ThreadLocal会引起内存泄露
- 为什么?
贴一张网图
如上图,ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用引用他,那么系统gc的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:
ThreadLocal Ref -> Thread -> ThreaLocalMap -> Entry -> value
永远无法回收,造成内存泄露。
- 怎么办?
- 手动调用ThreadLocal的remove函数,删除不使用的ThreadLocal。
- 将ThreadLocal设置为 private static (看下本文开始的译文),这样的话ThreadLocal的生命周期就更长,由于一直存在ThreadLocal的强引用,所以ThreadLocal也就不会被回收,也就能保证任何时候都能根据ThreadLocal的弱引用访问到Entry的value值,然后remove它,防止内存泄露。
参考链接: