ThreadLocal基础
ThreadLocal类可以让每个线程绑定自己的值,它就像一个全局存放数据的盒子,盒子中可以存放每个线程的私有数据。
ThreadLocal类只有一个无参的构造函数,因此实例化ThreadLocal的方法为: new ThreadLocal<T>();
threadLocal.get()方法,取当前线程存放在ThreadLocal里的数据;
threadLocal.set(T value)方法,设置当前线程在ThreadLocal里的数据;
threadLocal.remove()方法,移除当前线程在ThreadLocal里的数据;
threadLocal.initialValue(),返回当前线程在ThreadLocal里的初始值。
类InheritableThreadLocal可以在子线程中取得父线程继承下来的值:在创建子线程时,子线程会接收所有可继承的线程局部变量的初始值,以获得父线程所具有的值。通常,子线程的值与父线程的值是一致的;但是,通过重写这个类中的 childValue 方法,子线程的值可以作为父线程值的一个任意函数。
ThreadLocal源码分析
首先看ThreadLocal类的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);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
可以看到它首先获取当前线程对象,然后获取线程对象的threadLocals属性,如果threadLocals为null,就新建一个ThreadLocalMap对象赋值给它,如果不为null,则直接往其上塞值。由此我们可以看到,ThreadLocal本身并不存储数据,数据存储在Thread对象上面。Thread的threadLocals属性是一个ThreadLocal.ThreadLocalMap类型的非静态属性,ThreadLocalMap的实现如下:
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
...
可以看到ThreadLocalMap的Entry继承了WeakReference,它使用ThreadLocal对象作为key。
再来看ThreadLocal类的get方法:
public T get() {
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();
}
同样获取当前线程对象,然后获取这个Thread对象的threadLocals,如果不为空,就根据当前ThreadLocal对象key获取值并返回,如果为空,那就设置并返回初始值。
ThreadLocal使用demo
ThreadLocal可以用来存储线程级上下文,一个demo如下:
import java.util.TimeZone;
public class MyContext {
private ThreadLocal<String> tenantSpaceId;
private ThreadLocal<TimeZone> userTimeZone;
private static MyContext instance = new MyContext();
private MyContext() {
tenantSpaceId = new ThreadLocal<>();
userTimeZone = new ThreadLocal<>();
}
public static MyContext getInstance() {
return instance;
}
public void setTenantSpaceId(String tsi) {
tenantSpaceId.set(tsi);
}
public String getTenantSpaceId() {
return tenantSpaceId.get();
}
public void setUserTimeZone(TimeZone tz) {
userTimeZone.set(tz);
}
public TimeZone getUserTimeZone() {
return userTimeZone.get();
}
public void clear() {
tenantSpaceId.remove();
userTimeZone.remove();
}
}