为每一个使用变量的线程都提供一个变量值的副本,每个线程都可以独立地改变自己的副本,而不会和其他线程冲突。
ThreadLocal类有一个protected方法initialValue()和三个public方法get(),set(T),remove(),其他的私有方法暂不讨论,主要看一下3个公有方法的实现原理:
public void set(T value) {
Thread t = Thread.currentThread();
# 每个线程有自己维护的一个ThreadLocalMap
ThreadLocalMap map = getMap(t);
# 如果存在就把当前值放入线程的Map中,key为当前ThreadLocal类的实例
if (map != null)
map.set(this, value);
else
# 如果不存在就创建这个Map,然后将ThreadLocal实例作为key
createMap(t, value);
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
# 根据当前的ThreadLocal实例获得存入的value值
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
# 如果不存在就返回一个初始值
return setInitialValue();
}
public void remove() {
# 获得当前线程的Map
ThreadLocalMap m = getMap(Thread.currentThread());
# 根据当前ThreadLocal实例移除Map中的值
if (m != null)
m.remove(this);
}
ThreadLocal(以空间换时间)和线程同步机制(以时间换空间)都是为了解决多线程中相同变量的访问冲突问题。
从上面已经知道ThreadLocal主要是依据Thread.currentThread()拿到线程的ThreadLocalMap变量,进而得到之前set的值。但是在某些情况下,需要在线程中获得其他线程set的值,就可以使用InheritableThreadLocal,从名字看出来这个就是用来继承的。
Thread类中存在两个变量:
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
对于一个线程而言,threadLocals是给自己用的,而inheritableThreadLocals是给其子线程使用的。
查看new Thread()的源码:
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
......
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
this.stackSize = stackSize;
......
}
解释:线程A中新建一个子线程B,这时会判断线程A的inheritableThreadLocals的变量是否为null,如果不为空,就将线程A的inheritableThreadLocals变量赋给线程B的inheritableThreadLocals。
再看InheritableThreadLocal类的源码:
protected T childValue(T parentValue) {
return parentValue;
}
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
其实和ThreadLocal中的方法差不多,起主要作用的是重写的getMap(Thread)方法,获得的是inheritableThreadLocals。