ThreadLocal的基本概念
在并发编程中,多个线程访问同一个变量,可能会出现线程安全问题、为了保证在多线程环境下访问共享变量的安全性,通常在访问共享变量的时候加锁,以实现线程同步的效果。
使用同步锁机制保证多线程访问共享变量的安全性的原理如下图。该机制能够保证同一时刻只有一个线程访问共享变量,从而确保在多线程环境下访问共享变量的安全性。
另外,为了更加灵活地确保线程的安全性,JDK中提供了一个ThreadLocal类,ThreadLocal类能够支持本地变量。在使用ThreadLocal类访问共享变量时,会在每个线程的本地内存中保存一份副本。在多个线程同时对这个变量进行读写操作时,实际操作的时本地内存中的副本,多个线程之间互不干扰,从而避免了线程安全的问题。使用ThreadLocal访问共享变量的示意图如下:
ThreadLocal的核心原理
ThreadLocal能够保证每个线程操作的都是本地内存中的变量副本。在底层实现上,调用ThreadLocal的set()方法会将本地变量保存在具体线程的内存空间中,ThreadLocal并不负责存储具体的数据。
Thread源码
在Thread类的源码中,定义了两个ThreadLocal.ThreadLocalMap类型的成员变量,分别为threadLocals和inheritableThreadLocals.
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
在Thread类中定义成员变量threadLocals和inherittableThreadLocals,两者的初始值都为null,并且只有当线程第一次调用ThreadLocal或者InheritableThreadLocals的set()方法或者get()方法是才会实例化变量。
通过ThreadLocal为每个线程保存的本地变量不是存在ThreadLocal实例中的,而是存在调用线程的threadLocals变量中的。也就是说,调用ThreadLocal的set()方法存储的本地变量在具体线程的内存空间中,而ThreadLocal类支提供了set()和get()方法来存储和读取本地变量的值,当调用ThreadLocal类的set()方法时,把要存储的值存储在调用线程的threadLocals变量中,当调用ThreadLocal类的get()方法时,从当前线程的threadLocals变量中获取保存的值。
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);
}
从ThreadLocal类中的set()方法的源码可以看出,在set()方法中,会先获取调用set()方法的线程,然后使用当前线程对象作为key调用getMap()方法获取ThreadLocalMap对象,getMap()方法源码如下:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
可以看出调用getMap()方法获取的就是当前线程中定义的threadLocals成员变量。获取ThreadLocalMap对象后,判断该对象是否为空,不为空则把value设置到Thread类的threadLocals成员变量中。保存数据时传递的key为当前ThreadLocal的this对象,而传递的value为调用set()方法传递的值。
如果当前线程的ThreadLocals成员变量为空,则调用createMap()方法来实例化当前线程的threadLocals成员变量,并保存value值。createMap()方法源码如下:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
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()方法通过调用getMap()方法并传入当前线程来获取threadLocals成员变量,然后判断当前线程的threadLocals成员变量是否为空。如果不为空,则直接返回当前线程threadLocals成员变量中存储的本地变量的值。如果为空,则调用setInitialValue()方法初始化threadLocals成员变量的值。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;
}
setInitialValue()方法与set()方法大致相同,只不过setInitialValue()方法会先调用initialValue()方法来初始化value的值,最后返回value的值。initialValue()方法源码如下:
protected T initialValue() {
return null;
}
initialValue()方法直接返回null,方法的具体逻辑会交由ThreadLocal类中的子类实现。
remove()方法
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
根据调用的getMap()方法获取当前线程的threadLocals成员变量,如果当前线程的threadLocals成员变量不为空,则直接从当前线程的threadLocals成员变量中移除当前threadLocal对象对应的value值。