前言
先来看以下这样一段代码:
public class ThreadLocalDemo {
static class ThreadA implements Runnable {
private ThreadLocal<String> threadLocal;
public ThreadA(ThreadLocal<String> threadLocal) {
this.threadLocal = threadLocal;
}
@Override
public void run() {
threadLocal.set("A");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ThreadA输出:" + threadLocal.get());
}
static class ThreadB implements Runnable {
private ThreadLocal<String> threadLocal;
private ThreadLocal<Integer> keyStore;
public ThreadB(ThreadLocal<String> threadLocal) {
this.threadLocal = threadLocal;
this.keyStore = new ThreadLocal<>();
}
@Override
public void run() {
threadLocal.set("B");
keyStore.set(1);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ThreadB输出:" + threadLocal.get() + keyStore.get());
}
}
public static void main(String[] args) {
//两个不同的线程操作同一个 ThreadLocal 对象,实际上会
// 转成对当前线程内部的 ThreadLocalMap 对象进行操作
ThreadLocal<String> threadLocal = new ThreadLocal<>();
new Thread(new ThreadA(threadLocal)).start();
new Thread(new ThreadB(threadLocal)).start();
}
}
}
以上我们创建了两个不同的线程,传入的是同一个
ThreadLocal
对象,并且往当中设置了不同的值,最终发现这两个线程中的设置的值是不会相互影响的,那么这是如何做到的呢?线程是如何维护属于自己的变量副本的呢?通过源码分析可以知道其中的原因。
源码分析
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); //为当前线程创建一个`ThreadLocalMap`对象
}
ThreadLocalMap getMap(Thread t) {
//获取当前线程的 ThreadLocalMap 成员变量对象,Thread 类当中维护了一个 ThreadLocal.ThreadLocalMap 成员变量
return t.threadLocals;
}
//当前执行线程的 ThreadLocalMap 为空,则创建一个对象
void createMap(Thread t, T firstValue) {
//创建 ThreadLocalMap 对象
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
// ThreadLocalMap 构造函数
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//哈希表的默认大小为16
table = new Entry[INITIAL_CAPACITY];
//哈希算法:根据key计算数据应该存在应该在哈希桶数组的哪个位置
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
get 方法:
//根据当前的ThreadLocal对象(key)获取数据
public T get() {
//获取当前执行的线程对象
Thread t = Thread.currentThread();
//获取 ThreadLocalMap 对象
ThreadLocalMap map = getMap(t);
//如果当前线程的ThreadLocalMap不为空
if (map != null) {
//根据键获取 Entry <key,value>对象
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T) e.value;
return result;
}
}
//返回初始值
return setInitialValue();
}
通过以上的分析,可以画出如下的运行示意图:
结论:执行同一个 ThreadLocal 对象的 get 或 set 方法,本质上是去操作当前执行线程持有的 ThreadLocalMap 对象(当前执行线程(Thread 类)中维护了一个 ThreadLocal.ThreadLocalMap 对象)。 ThreadLocalMap 是一个哈希表,用于存储键值对,可以快速的根据 key 查找出 value,其中,key是当前的 ThreadLocal 对象,value 是我们外部传入的值。
个人博客:https://www.kangpeiqin.cn/#/index