《Java源码分析》:ThreadLocal /ThreadLocalMap
首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set(Object obj) 到线程中的对象obj是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。
线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。
另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本。
ThreadLocal适用于资源共享但不需要维护状态的情况,也就是一个线程对资源的修改,不影响另一个线程的运行;这种设计是‘空间换时间’,synchronized顺序执行是‘时间换取空间’。
在分析源码之前,先来介绍下它的内部工作原理。
假设我们在某个类中定义了一个ThreaLocal对象threadLocal。如下:
private static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
上面我们总说每个线程有一个ThreadLocal的拷贝。具体到底是怎么样的呢??
是这样的:
1、首先每个线程都有一个私有的ThreadLocalMap的引用map
2、当我们第一次调用threadLocal.set()/threadLocal.get()方法的时候会对这个map进行初始化。
3、这个ThreadLocalMap就是用来保存(threadLocal,value)这样的键值对的,即每个使用threadLocal的线程都是将其作为键而指定的值作为value保存在这个map中,而map是每个线程私有的,因此是独立的,可以随意操作。
当我们调用threadLocal.get()方法时,他首先会拿到当前线程的ThreadLocalMap对象map,由于这个ThreadLocalMap对象map中保存了(threadLocal,value)的键值对,因此根据map.get(threadLocal)来拿到相应的value值,这样我们就可以随意来操作这个value,不会影响其他线程中的value。
不知道这样说清晰了没有,如果没有,那我们就来看下ThreadLocal源码的实现。
下面我们从源码的角度来看下ThreadLocal是如何来工作的。
1、ThreadLocal的构造方法
public ThreadLocal() {
}
ThreadLocal只有这样一个构造方法。
2、ThreadLocal的get方法介绍
源码如下:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); //得到当前线程的ThreadLocalMap对象
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this); //根据ThreadLocal得到键值对对象Entry。
//检查是否为空,如果不为空,则取出value即可,如果为空,则说明此ThreadLocal随关联的键值对被垃圾回收了,这里就再一次的调用了setInitialValue()方法进行初始化
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();//利用默认值来为当前线程创建一个ThreadLocalMap对象,如果没有重写inititalValue(),则默认值为null.
}
get()方法的功能:返回当前线程的ThreadLocal副本中的值value,如果当前线程的这个变量没有值,则首先调用inititalValue这个方法来初始化value。
下面我们就来详细的分析下这个get方法中的代码:
1、Thread t = Thread.currentThread();
这行代码就是获取当前工作线程。即正在调用threadLocal.get()方法的线程。
2、 ThreadLocalMap map = getMap(t);
这行代码就是得到当前线程的ThreadLocalMap对象。每个线程都有一个ThreadLocalMap对象。
下面我们就先来看下这个getMap(t)方法
//返回与ThreadLocal关联的ThreadLocalMap,每个线程对应一个ThreadLocalMap对象。
ThreadLocalMap getMap(Thread t) {
return t.t