ThreadLocal是什么
java.lang.ThreadLocal是jdk提供的一个可以用来保存线程本地变量的类,它可以确保线程不会访问到属于其它线程的变量
ThreadLocal应用场景
1.线程隔离
常用案例是使用ThreadLocal为每个线程绑定一个会话、数据库连接等。比如通过ThreadLocal可以让每一个线程都使用自己的数据库连接,防止数据库连接混用造成的操作问题
2.在函数间传递参数
将需要的参数放入ThreadLocal,需要的时候再从ThreadLocal获取
ThreadLocal如何保证线程变量的隔离
每个线程实例中都会有一个ThreadLocalMap对象,线程本地变量其实就是保存在线程实例的ThreadLocalMap对象中的。因为每个线程都有自己的ThreadLocalMap,所以可以保证线程变量的隔离
通过ThreadLocal.set()方法设置线程本地变量就是在ThreadLocalMap中增加一个key-value条目,其中key是ThreadLocal对象的一个弱引用,value是设置的本地变量,下面是ThreadLocal类的set和get操作的源码
public void set(T value) {
Thread t = Thread.currentThread();
//获取当前线程ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
//ThreadLocalMap不为空则设置value,并且将this作为key
map.set(this, value);
} else {
//ThreadLocalMap为空则创建Map,设置value
createMap(t, value);
}
}
public T get() {
Thread t = Thread.currentThread();
//获取当前线程的ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
if (map != null) {
//从ThreadLocalMap对象中获取Entry条目
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocalMap为什么要使用ThreadLocal对象的弱引用作为Key
使用弱引用是为了防止内存泄露问题,出现内存泄露问题需要满足以下条件:
- Entry条目使用强引用
- 线程不会死亡(现在大多使用线程池复用线程资源)
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
//Entry类继承了WeakReference
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
//这里表示Entry持有的是ThreadLocal对象的弱引用
super(k);
value = v;
}
}
}
考虑有以下代码
public void threadLocal() {
//新建一个ThreadLocal,local是一个强引用
ThreadLocal<String> local = new ThreadLocal<>();
//设置一个值,此时当前线程的ThreadLocalMap对象中的Entry持有一个local指向对象的一个弱引用
local.set("test");
...
}
当threadLocal方法执行完成后,local变量将被释放,此时local变量指向的对象只有一个引用,即当前线程的ThreadLocalMap对象中的Entry持有的弱引用。系统发生gc时,只有弱引用的对象将会被回收,此时Entry条目中的key就被回收了。因为在调用ThreadLocal的set、get、remove方法时会清除key被回收的数据,所以不会造成内存泄露
现在使用ThreadLocal都会使用private static final修饰,这种情况下ThreadLocal会一直存在一个强引用,所以一定要记得调用remove方法