1. ThreadLocal内部结构
在早期的JDK版本中,ThreadLocal的内部结构是一个Map,其中每一个线程实例作为Key,线程在“线程本地变量”中绑定的值为Value,每一个ThreadLocal实例拥有一个Map实例。
在JDK 8版本中,每一个Thread线程内部都有一个ThreadLocalMap,其中ThreadLocal实例为Key,本地数据为Value。如果给一个Thread创建多个ThreadLocal实例,然后放置本地数据,那么当前线程的ThreadLocalMap中就会有多个“Key-Value对”
从代码的层面来说,新版本的ThreadLocalMap还是由ThreadLocal类维护的,由ThreadLocal负责ThreadLocalMap实例的获取和创建,并从中设置本地值、获取本地值。所以ThreadLocalMap还寄存于ThreadLocal内部,并没有被迁移到Thread内部。
每一个线程在获取本地值时,都会将ThreadLocal实例作为Key从自己拥有的ThreadLocalMap中获取值,别的线程无法访问自己的ThreadLocalMap实例,自己也无法访问别人的ThreadLocalMap实例,达到相互隔离,互不干扰。
@Data
public class Person {
private int age = 10;
}
public class TestThreadLocal {
private static final ThreadLocal<Person> threadLocal = new ThreadLocal<Person>();
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
// 提交5个任务,使用5个线程
for(int i=0;i<5;i++){
executorService.execute(new Runnable() {
@Override
public void run() {
// 获取threadLocal中当前线程绑定的值
if(threadLocal.get()==null){
// 设置threadLocal中当前线程绑定的值
threadLocal.set(new Person());
}
System.out.println("初始的本地值:"+threadLocal.get());
// 每个线程执行10次
for(int i=0;i<10;i++){
Person foo = threadLocal.get();
foo.setAge(foo.getAge()+1);
// 睡眠1s
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("累加10次之后的本地值:"+threadLocal.get());
// 删除threadLocal中当前线程所绑定的值,这点对于线程池中的线程特别重要
threadLocal.remove();
}
});
}
}
}
2. ThreadLocal源码分析
1. Thread类源码:
public class Thread implements Runnable {
// 每一个Thread线程内部都有一个Map(ThreadLocalMap)
// 如果给一个Thread创建多个ThreadLocal实例,然后放置本地数据,
// 那么当前线程的ThreadLocalMap中就会有多个“Key-Value对”,
// 其中ThreadLocal实例为Key,本地数据为Value。
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}
2. ThreadLocal类源码:
ThreadLocal源码提供的方法不多,主要有:set(T value)方法、get()方法、remove()方法。
1. set(T value)方法
set(T value)方法用于设置“线程本地变量”在当前线程的ThreadLocalMap中对应的值,相当于设置线程本地值,其核心源码如下:
public void set(T value) {
// 获取当前线程对象
Thread t = Thread.currentThread();
// 获取当前线程的ThreadLocalMap成员
ThreadLocalMap map = getMap(t);
// 如果map不为null,将value绑定到threadLocal实例上(threadLocal,value)
if (map != null)
map.set(this, value);
else
// 如果当前线程没有ThradLocalMap成员实例,创建一个ThreadLocalMap实例,然后作为成员关联到t
createMap(t, value);
}
// 获取线程t的ThreadLocalMap成员
// 每一个线程在获取本地值时,都会将ThreadLocal实例作为Key从自己拥有的ThreadLocalMap中获取值
// 别的线程无法访问自己的ThreadLocalMap实例,自己也无法访问别人的ThreadLocalMap实例,达到相互隔离,互不干扰
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
// 线程t创建一个ThreadLocalMap成员
// 并为新的map成员设置第一个key-value对,key为当前的ThreadLocal实例
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
通过以上源码可以看出set(T value)方法的执行流程,大致如下:
(1)获得当前线程,然后获得当前线程的ThreadLocalMap成员,暂存于map变量。
(2)如果map不为空,就将Value设置到map中,当前的ThreadLocal作为Key。
(3)如果map为空,为该线程创建map,然后设置第一个“Key-Value对”,Key为当前的ThreadLocal实例,Value为set()方法的参数value值。
2. get()方法
get()方法用于获取“线程本地变量”在当前线程的ThreadLocalMap中对应的值,相当于获取线程本地值,其核心源码如下:
public T get() {
// 获取当前线程对象
Thread t = Thread.currentThread();
// 获取线程对象的ThreadLocalMap成员
ThreadLocalMap map = getMap(t);
// 判断map是否为null
if (map != null) {
// 以当前threadLocaL为key获取值
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// 如果当前线程对应的map不存在
// 或者当前线程对应的map存在,但是当前ThreadLocal中没有对应的key-value对,返回初始值
return setInitialValue();
}
// 设置ThreadLocal关联的初始值并返回
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;
}
通过以上源码可以看出T get()方法的执行流程,大致如下:
(1)先尝试获得当前线程,然后获得当前线程的ThreadLocalMap成员,暂存于map变量。
(2)如果获得的map不为空,那么以当前ThreadLocal实例为Key尝试获得map中的Entry(条目)。
(3)如果Entry不为空,就返回Entry中的Value。
(4)如果Entry为空,就通过调用initialValue初始化钩子函数获取ThreadLocal初始值,并设置在map中。如果map不存在,还会给当前线程创建新ThreadLocalMap成员,并绑定第一个“Key-Value对”。
3. remove()方法
remove()方法用于在当前线程的ThreadLocalMap中移除“线程本地变量”所对应的值,其核心源码如下:
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
3. ThreadLocalMap源码分析
ThreadLocal的操作都是基于ThreadLocalMap展开的,而ThreadLocalMap是ThreadLocal的一个静态内部类,其实现了一套简单的Map结构。
ThreadLocalMap的成员变量与HashMap的成员变量非常类似,其内部的主要成员如下:
static class ThreadLocalMap {
// map的初始容量
private static final int INITIAL_CAPACITY = 16;
// map的条目数,作为哈希表使用
private Entry[] table;
// map的条目数
private int size = 0;
// 扩容因子
private int threshold; // Default to 0
}
ThreadLocal源码中的get()、set()、remove()方法都涉及ThreadLocalMap的方法调用,主要调用了ThreadLocalMap的如下几个方法:
(1)set(ThreadLocal<?> key,Object value)
:向Map实例设置“Key-Value对”。
(2)getEntry(ThreadLocal)
:从Map实例获取Key(ThreadLocal实例)所属的Entry。
(3)remove(ThreadLocal)
:根据Key(ThreadLocal实例)从Map实例移除所属的Entry。