public T get() {
Thread t = Thread.currentThread();//取得当前线程
ThreadLocalMap map = getMap(t);// 获取该线程对应的ThreadLocalMap
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//获取到键值对,注意这里获取键值对传进去的是this,而不是当前线程t。
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
如果获取成功,则返回value值。如果map为空,则调用setInitialValue方法并返回value。
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
ThreadLocalMap这个类是ThreadLocal类的一个内部类:
public class ThreadLocal {
。。。。。。
static class ThreadLocalMap {
static class Entry extends WeakReference> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal> k, Object v) {
super(k);
value = v;
}
}
。。。。。。
}
可以看出ThreadLocalMap的Entry继承了WeakReference,并且使用ThreadLocal作为键值。
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
protected T initialValue() {
return null;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
在setInitialValue方法中,会调用protected修饰的initialValue()方法, 默认情况下,initialValue方法返回的是null。所以在进行get之前,必须先set,否则会报空指针异常,如果想在get之前不需要调用set就能正常访问的话,必须重写initialValue()方法。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
总的来说,实际上是通过ThreadLocal创建的副本是存储在每个线程自己的threadLocals中,threadLocals的类型ThreadLocalMap的键值为ThreadLocal对象,这样每个线程中可有多个threadLocal变量。
public class ThreadLocalTest {
ThreadLocal a = new ThreadLocal<>();
ThreadLocal b = new ThreadLocal() {
protected String initialValue() {
return Thread.currentThread().getName() + "---zero";
}
};
public static void main(String[] args) throws InterruptedException {
final ThreadLocalTest test = new ThreadLocalTest();
System.out.println(test.a.get());
System.out.println(test.b.get());
Thread thread1 = new Thread() {
public void run() {
test.a.set(new StringBuilder("hello"));
System.out.println(test.a.get());
System.out.println(test.b.get());
};
};
thread1.start();
thread1.join();
System.out.println(test.a.get());
System.out.println(test.b.get());
}
}
运行结果:
null
main---zero
hello
Thread-0---zero
null
main---zero
InheritableThreadLocal类继承于ThreadLocal类,所以它具有ThreadLocal类的特性,但又是一种特殊的 ThreadLocal,其特殊性在于InheritableThreadLocal变量值会自动传递给所有子线程,即在创建子线程时,子线程会接收所有可继承的线程局部变量的初始值,以获得父线程所具有的值。而普通ThreadLocal变量不行。
如果一个子线程调用InheritableThreadLocal的get(),那么它将与它的父线程看到同一个对象。为保护线程安全性,应该只对不可变对象(一旦创建,其状态就永远不会被改变的对象)使用InheritableThreadLocal,因为对象被多个线程共享。简单示例:
public class InheritableThreadLocalTest {
InheritableThreadLocal a = new InheritableThreadLocal<>();
InheritableThreadLocal b = new InheritableThreadLocal();
public static void main(String[] args) throws InterruptedException {
final InheritableThreadLocalTest test = new InheritableThreadLocalTest();
test.a.set(new StringBuilder("main---hello"));
test.b.set("main---zero");
System.out.println(test.a.get());
System.out.println(test.b.get());
Thread thread1 = new Thread() {
public void run() {
System.out.println(test.a.get());
System.out.println(test.b.get());
StringBuilder a = test.a.get().append("---subThread");
test.a.set(a);
test.b.set("subThread--zero");
System.out.println(test.a.get());
System.out.println(test.b.get());
};
};
thread1.start();
thread1.join();
System.out.println(test.a.get());
System.out.println(test.b.get());
}
}
运行结果:
main---hello
main---zero
main---hello
main---zero
main---hello---subThread
subThread--zero
main---hello---subThread
main---zero
可以看出,如果InheritableThreadLocal存储的是可变性(mutable)的对象,如StringBuilder,对于主线程设置的值子线程可以通过get函数获取,但子线程调用set函数设置新值后,对主线程没有影响,对其它子线程也没有影响,只对自己可见,但如果子线程先get获取再修改对象的属性,那么这个修改对主线程和其它子线程是可见的,因为共享的是同一个引用。为了保护线程的安全性,一般建议只传递不可变(Immuable)对象,即没有状态的对象。