共计 4328 个字符,预计需要花费 11 分钟才能阅读完成。
一、前言
多个线程应用共享变量时,如果要保障线程平安,通常会加锁,synchronized 或者 Lock。但这两种锁都是重量级的,如果多个线程都心愿操作各自的变量,彼此间互不影响,那么 ThreadLocal 就派上用场了,InheritableThreadLocal 则是它的性能扩大,前面会剖析它的应用场景。
二、ThreadLocal 实现原理
先来看下它的类构造:
红色框中的是咱们罕用的办法,它外部是用 ThreadLocalMap 实现的,尽管命名有 Map 后缀,但并没有实现 Map 接口,来看下它的构造:
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {super(k);
value = v;
}
}
private Entry[] table;}
能够看到,ThreadLocalMap 外部是用 Entry[] 来保留线程变量的,key 是 ThreadLocal 实例自身,并不是以后线程 Thread 哦,value 就是要用的变量。
先来看 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);
}
①先获取以后线程 t 对应的 ThreadLocalMap 实例,getMap 代码如下:
ThreadLocalMap getMap(Thread t) {return t.threadLocals;}
能够看到,Thread 外部有个 ThreadLocalMap 类型的援用
Thread.java
ThreadLocal.ThreadLocalMap threadLocals = null;
②如果 map 不为空,保留值,this 即为以后 ThreadLocal 实例,value 为咱们要用的变量
③如果 map 为空,则 createMap,代码如下:
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);
}
来看看 ThreadLocalMap 的构造函数做了哪些事件:
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {// 初始化 Entry[]
table = new Entry[INITIAL_CAPACITY];
// 计算 firstValue 放在该数组的地位 i
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
// 赋值
table[i] = new Entry(firstKey, firstValue);
size = 1;
// 设置阈值,扩容会用到
setThreshold(INITIAL_CAPACITY);
}
setThreshold 办法如下:
/**
* Set the resize threshold to maintain at worst a 2/3 load factor. */
private void setThreshold(int len) {threshold = len * 2 / 3;}
代码也很简略,看正文就能明确,这里的实现思路和 HashMap 差不多。
再来看 get() 办法:
public T get() {
// 获取以后线程 t
Thread t = Thread.currentThread();
// 获取以后线程 t 对应的 ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
// 获取以后 ThreadLocal 实例对应的线程变量
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// map 为空则初始化
return setInitialValue();}
setInitialValue() 代码如下:
private T setInitialValue() {
// 获取设定的初始化值,默认为 null,用户能够调用它来设置初始值
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
// map 不为空则设置初始化值,否则创立 map
if (map != null)
map.set(this, value);
else createMap(t, value);
return value;
}
最初看下 remove() 办法:
public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());
// 删除以后 ThreadLocal 实例对应的变量
if (m != null)
m.remove(this);
}
罕用的 set(),get(),remove() 办法逻辑都很清晰明了,就不做赘述了
三、InheritableThreadLocal 的作用
咱们晓得,ThreadLocal 在应用过程中,各个线程之间的变量是互不影响的,子线程没法拿到父线程的本地变量,这也是失常的。但有时候也有这样的需要场景,子线程须要拿到父线程的变量,比方子线程须要应用寄存在 ThreadLocal 变量中的用户登录信息,再比方一些中间件须要把对立的 id 追踪的整个调用链路记录下来。那么 InheritableThreadLocal 就能够做这个事件。上面来看看它是怎么实现的吧。
public class InheritableThreadLocal<T> extends ThreadLocal<T> {protected T childValue(T parentValue) {return parentValue;}
ThreadLocalMap getMap(Thread t) {return t.inheritableThreadLocals;}
void createMap(Thread t, T firstValue) {t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
能够看到,InheritableThreadLocal 类继承了 ThreadLocal,并重写了 getMap() 和 createMap(),外面呈现了个 t.inheritableThreadLocals,它就是实现共享变量的要害,它替换了 t.threadLocals, 所有原先对 t.threadLocals 的操作都改成了 t.inheritableThreadLocals。它和 t.threadLocals 类型一样,也是 Thread 的一个属性。
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
但目前如同还是看不出来起共享成果的是哪段代码?它暗藏的很深,在 Thread 的构造函数里,来看看吧:
public Thread(Runnable target) {init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
...
// parent 指的是调用 new Thread() 的线程,即 main 线程
Thread parent = currentThread();
...
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
...
}
为了不便解说,省略了些代码,只看对 inheritableThreadLocals 起作用的。
来看看 ThreadLocal.createInheritedMap() 代码:
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {return new ThreadLocalMap(parentMap);
}
private ThreadLocalMap(ThreadLocalMap parentMap) {Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (int j = 0; j < len; j++) {Entry e = parentTable[j];
if (e != null) {@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) = e.get();
if (key != null) {// childValue() 就是父线程的 e.value
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
能够看到,构造函数就是把父线程的 ThreadLocalMap 里的 Entry[] 拷贝到子线程中,实现变量的共享。
到这里本文就完结了,谢谢观看。