ThreadLocal介绍
线程本地变量,修饰的变量在线程间独立,互不影响。
ThreadLocal<String> local = new ThreadLocal<>();
local.set("caojiantao");
new Thread(() -> {
local.set("chenlisha");
}).start();
new Thread(() -> {
// null
System.out.println(local.get());
}).start();
通常有以下几个应用场景:
- 保存请求用户态(拦截器)
- 动态数据源读写分离(AbstractRoutingDataSource)
- 数据库连接(事务性)
原理图解
着重描述下ThreadLocalMap;
- 是
Thread的私有变量,线程间隔离 - 未实现
Map接口,JDK自实现的简易Map Entry继承自WeakReference,对应的Key是弱引用Key为ThreadLocal实例- 通过开放寻址法解决hash冲突
为什么是弱引用
弱引用:只具有弱引用的对象在GC时会被回收。
总结:弱应用+实时清除,避免弱应用问题。
ThreadLocal是弱引用,在GC没有其他引用时内存会被回收;ThreadLocal在get、set时都会清除key为null的Entry;
如果不是弱引用,当ThreadLocal不可触达时,关联的value也无法触达,刚好线程生命周期长(线程池)的话,由于ThreadLocalMap强引用的原因,value一直不会被释放从而造成内存泄露。
ThreadLocal.set
public void set(T value) {
// 获取当前线程关联的ThreadLocalMap
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
// 如果为空需要初始化
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocal.get
public T get() {
// 获取当前线程关联的ThreadLocalMap
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
// 如果为空返回默认值(可重载方法修改)
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocal.remove
public void remove() {
// 获取当前线程关联的ThreadLocalMap
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
// 清除当前key(ThreadLocal)和value(Object)
m.remove(this);
}
为了避免内存泄漏,一定要触发remove方法。
ThreadLocalMap
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
// ThreadLocal关联的对象
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
// 开放寻址法,没有链表
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
// ...
private void rehash() {
// 删除空的entry节点
expungeStaleEntries();
if (size >= threshold - threshold / 4)
// 2倍扩容
resize();
}
}
补充:InheritableThreadLocal
Inheritable,可继承的,意为可继承的ThreadLocal。通常用来父子线程传递参数,例如请求userId。
InheritableThreadLocal<String> inheritable = new InheritableThreadLocal<>();
inheritable.set("caojiantao");
new Thread(() -> {
// caojiantao
System.out.println(inheritable.get());
}).start();
看看InheritableThreadLocal的源码(部分);
// 继承自ThreadLocal并重写了两个方法
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
// InheritableThreadLocal也是Thread的一个私有变量
@Override
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
// 实例初始化
@Override
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
如何传递参数给子线程,那就看看Thread的init方法(部分);
private void init(... boolean inheritThreadLocals) {
...
Thread parent = currentThread();
// inherit默认为true
if (inheritThreadLocals && parent.inheritableThreadLocals != null) {
// 默认将父线程的inheritable浅拷贝了一份到子线程中
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}
...
}
值得注意的是,线程池复用的线程不会重复触发Thread的init方法,会导致参数丢失。