ThreadLocal类是啥
使用ThreadLocal
类,目的是为了得到线程隔离的变量
那么,ThreadLocal
类到底是啥?
其实,它就是个普通的类
那它又是如何实现我们希望的功能:得到线程隔离的变量呢?
其实很简单,每个Thread
里都有一个线程独有的map
,它的key
就是ThreadLocal
类的实例
我们通过ThreadLocal
去获取线程隔离的变量,其实就是去这个map
里把ThreadLocal
的实例作为key
去找到对应的value
罢了
具体代码
首先,让我们看看ThreadLocal是怎么用的
ThreadLocal<String> tl = new ThreadLocal<>();
//设置变量
tl.set("hello");
//获取变量
tl.get();
用法很简单,但内部是如何实现的呢?往下看
Thread类
//每个thread里都有一个ThreadLocalMap,里面存着当前线程独有的变量
ThreadLocal.ThreadLocalMap threadLocals = null;
可以看出,每个thread都有个ThreadLocalMap,这里面就是该线程独有的变量集合
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 getMap(Thread t) {
return t.threadLocals;
}
ThreadLocal.set
方法,把当前线程的ThreadLocalMap
拿到了,再调用ThreadLocalMap
的set
方法设置变量,非常简单。
那么下一步就要看看ThreadLocalMap
是怎么set
的了
ThreadLocalMap
public class ThreadLocal<T> {
static class ThreadLocalMap {
private Entry[] table;
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//其余省略
}
//其余省略
}
可以看出,这个结构还是比较复杂的
ThreadLocalMap
是ThreadLocal
的内部类Entry
又是ThreadLocalMap
的内部类Entry
里维护的value
就是我们set
的变量
ThreadLocalMap的本质,就是维护了一个Entry[]
,ThreadLocalMap
一定是通过某种方式,把key
(也就是ThreadLocal
实例)对应到Entry
数组的某个index
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
从上面代码可以看出,它是通过key.threadLocalHashCode & (len-1)
来取到index
的,然后就是设置或更新操作
梳理一下整个步骤:
- 调用某
ThreadLocal
对象的set
方法 ThreadLocal
对象通过Thread.currentThread
取到当前线程t
- 取到当前线程
t
的ThreadLocalMap
- 把
ThreadLocal
对象作为key
,ThreadLocalMap
通过key.threadLocalHashCode & (len-1)
计算出value
应该放在Entry
数组的哪个index
- 更新或插入新的
Entry
对象到index
位置,Entry
对象包含了要set
的value