线程局部变量就是为每一个使用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立地根据该线程局部变量改变这个变量值副本,而不会和其他线程的副本发生冲突。
其实就是每一个线程类都有一个ThreadLocalMap类(该类是ThreadLocal的静态内部类)的对象,将线程局部变量和对应的变量值副本以键值对的形式存在Map集合中。
假设有如下简单案例:
public class ThreadLocalTest implements Runnable {
private static ThreadLocal<Person> threadLocal = new ThreadLocal<>();
@Override
public void run() {
Person person = threadLocal.get();
if (null == person) {
person = new Person();
threadLocal.set(person);
}
}
public static void main(String[] args) {
ThreadLocalTest t = new ThreadLocalTest();
Thread t1 = new Thread(t, "Thread A");
Thread t2 = new Thread(t, "Thread B");
t1.start();
t2.start();
}
}
线程t1和线程t2都使用的线程局部变量threadLocal,当线程t1需要获取该线程局部变量threadLocal的value时,通过调用threadLocal.get()方法得到对应的value值。threadLocal.get()方法的底层代码其实是:
1、先获得使用该threadLocal对象的当前线程对象,判断当前线程对象的ThreadLocalMap集合是否为空;
2、如果该ThreadLocalMap集合不为空,就根据该threadLocal对象获取对应的key-value对象e;
3、若该Entry类的对象e不为null,就返回threadLocal对象对应的value。
4、如果该ThreadLocalMap集合为空,就调用setInitialValue()方法。
该方法是得到一个初始值(这个初始值由我们自己定义,比如返回案例中的Person对象。其实就是重写ThreadLocal类中的initialValue()方法,默认返回null),并将threadLocal对象和该初始值存入ThreadLocalMap集合中,这样下次调用get()方法时,直接根据key就可以得到value。
也可以自己定义初始值:
通过createMap()方法,将ThreadLocalMap集合添加到每个线程中,比如t1、t2都各有一个ThreadLocalMap集合。
回到案例中,线程t1和t2都是用同一个threadLocal对象,但是在各自的本地线程中因为各有ThreadLocalMap集合,因此对于同一个key,可以设置不一样的value,得到的value值也是不一样的。
例如 Strut2 的 ActionContext,同一段代码被不同的线程调用运行时,该代码操作的数据是每个线程各自的状态和数据,对于不同的线程来说, getContext 方法拿到的对象都不相同,对同一个线程来说,不管调用 getContext 方法多少次和在哪个模块中 getContext 方法,拿到的都是同一个。
ThreadLocal 用于实现多线程内的数据共享,即对于相同的程序代码,多个模块在同一个线程中运行时要共享一份数据,而在另外线程中运行时又共享另外一份数据。