在jdk的api文档中,是这么描述ThreadLocal类的:This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID)。大概意思是说:ThreadLocal类是用于存储线程本地变量的,这些线程本地变量是通过ThreadLocal对象的get和set方法获取和存放的。ThreadLocald对象在一个类中一般是私有的静态的变量,存储那些和线程状态的变量,比如用户id或者事务id。api中还提供了一个简单的实例,在这里贴出来方便理解ThreadLocal的作用和使用。
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadId {
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID
//这里重写ThreadLocal的方法,在下面调用threadId.get(),由于没有初始化
//会调用initalValue方法初始化value,并将value返回
private static final ThreadLocal<Integer> threadId =
new ThreadLocal<Integer>() {
@Override protected Integer initialValue() {
return nextId.getAndIncrement();
}
};
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
}
在上面这个例子中就是通过ThreadLocal对象threadId设置一个id作为线程id,然后通过get方法获取线程id。这样,每个线程都会有唯一一个ID,即每个线程持有线程本地变量。那么在ThreadLocal类内部中是怎样存储和获取一个线程本地变量的呢。下面通过源码分析ThreadLocal的工作原理。
首先,来看Thread这个类
Thread定义了一个ThreadLocal.ThreadLocalMap的成员变量threadLocals。注意了,这个threadLocals对象是存储线程本地变量的载体,即一个线程的所有ThreadLocal对象的变量存储在threadLocals这个对象中,其中ThreadLocal对象为key,线程本地变量为value。
下面来看看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);
}
getMap()方法的源码如下:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
set方法中先获取当前线程对象,然后获Thread线程对象中的threadLocals变量,也就是在上文提到的存储线程本地变量的载体。如果这个threadLocals对象是非null,就以ThreadLocal对象引用为key,保存线程本地变量。以文章开头的例子来说,就是将threadId对象引用作为key,将nextId.getAndIncrement()的值作为value存储。
再看看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();
}
get方法也是先获取当前线程对象,在获取线程对象中的threadLocals变量。若threadlocals非null,以当前ThreadLocal对象引用为key,获取变量。若为null,执行setInitialValue()方法。来看看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;
}
在该方法中会调用initialValue()方法,这个方法默认返回null,所以如果不重写这个方法,又不调用set()方法设置值,调用get()方法就会返回null。文章开头的例子重写了该方法。
总结:ThreadLocal工作的原理就是通过以自身引用为key,将待要保存的线程变量保存在Thread对象的成员变量threadLocals中。