ThreadLocal简介
ThreadLocal是线程内部的局部变量,保证该变量在线程内部是独立的,我们可以通过set/get方法来设置或者获取变量的值。
根据ThreadLocal结构,我们画了下面一张流程结构图:
结构图说明:
1)Thread里面有一个ThreadLocal.ThreadLocalMap的变量threadLocals,threadLocals是一个Map结构,key是ThreadLocal<T>,value是T。
2)假设有两个ThreadLocal变量,分别是ThreadLocal<A> tlA,ThreadLocal<B> tlB。
在线程Thread-1和Thread-2中tlA.set(new A()),tlB.set(new B()),则在线程Thread-1和Thread-2中通过tlA.get(),tlB.get()获取到的分别是相应线程对应的对象A,对象B。
总结: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);
}
说明:我们先拿到当前的线程对象,然后通过线程对象取到线程的ThreadLocalMap对象,即threadLocals,然后分如下两种情况:
1)threadLocals是null,则创建ThreadLocalMap对象,同时将key值和value放到map中;
2)threadLocals非null,则直接设定key值和value值。
我们上面也说到了,threadLocals是一个ThreadLocal.ThreadLocalMap类型的Map结构,key是ThreadLocal<T>,value是T。
get方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
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;
}
说明:get方法我们先拿到当前的线程对象,然后通过线程对象取到线程的ThreadLocalMap对象,即threadLocals,然后也分如下两种情况:
1)threadLocals是null,则创建ThreadLocalMap对象,同时设定一个初始值;
2)threadLocals非null,则直接以当前的ThreadLocal<T>对象取到value并返回。
ThreadLocal例子
ThreadLocal的例子很简单,我们声明了两个ThreadLocal变量,然后分别在主线程,两个子线程里面通过set方法设定对象的值,并在相应的线程里面通过get方法输出对象的hashcode,代码如下:
public class ThreadLocalTest {
private static ThreadLocal<A> tlA = new ThreadLocal<>();
private static ThreadLocal<B> tlB = new ThreadLocal<>();
public static class A {
@Override
public String toString() {
return A.class.getSimpleName() + ":" + Integer.toHexString(hashCode());
}
}
public static class B {
@Override
public String toString() {
return B.class.getSimpleName() + ":" + Integer.toHexString(hashCode());
}
}
public static class ThlRunnable implements Runnable {
@Override
public void run() {
tlA.set(new A());
tlB.set(new B());
System.out.println("ThreadName:" + Thread.currentThread().getName() + ","
+ tlA.get().toString() + "," + tlB.get().toString() );
}
}
public static void main(String[] args) {
tlA.set(new A());
tlB.set(new B());
for (int i = 0; i < 2; i++) {
new Thread(new ThlRunnable()).start();
}
System.out.println("ThreadName:" + Thread.currentThread().getName() + ","
+ tlA.get().toString() + "," + tlB.get().toString() );
System.exit(0);
}
}
输出结果:
ThreadName:Thread-1,A:5012d748,B:5ed5333a
ThreadName:main,A:3feba861,B:5b480cf9
ThreadName:Thread-2,A:18cf66fa,B:16089b93
每个线程获取的对象结果都是不一样,因此,通过ThreadLocal,我们保证了同一类型的不同对象在各个线程之间的独立性。