什么是ThreadLocal? ThreadLocal官方介绍是线程内部存储类,各个线程私有的变量可用于ThreadLocal进行存取,线程之间的变量是不可见的
ThreadLocal源码分析
ThreadLocal.set(T)方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
从上面代码可能会对ThreadLocal有错误的理解,ThreadLocal内部维护一个map,用当前线程作为key,value作为值进行存储,这是完全错误。实际上ThreadLocal使用并不是一个map,而是线程维护ThreadLocalMap对象进行存储变量的,ThreadLocalMap内部维护一个Entry对象数组,Entry对象存放着弱引用的当前ThreadLocal对象和实际的value值。
初始化ThreadLocalMap内部Entry对象数组
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//初始化Entry对象数组长度为16
table = new Entry[INITIAL_CAPACITY];
//当前ThreadLocal对象进行hash运算&(数组长度-1)得到数组下标
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//创建Entry对象存放在对应数组下标位置
table[i] = new Entry(firstKey, firstValue);
size = 1;
//设置加载因子 数组容量*2/3
setThreshold(INITIAL_CAPACITY);
}
详细看下如何创建Entry对象
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
//调用父类构造方法
super(k);
value = v;
}
}
-------------------------------super(k);-------------------------------------------------
public WeakReference(T referent) {
super(referent);
}
-------------------------------super(referent);------------------------------------------
Reference(T referent) {
this(referent, null);
}
//最终当前ThreadLocal实例对象存在再此处referent
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
从上面代码可以看出ThreadLocal内部维护ThreadLocalMap对象,ThreadLocalMap有Entry[] 属性用于存在线程变量,当前ThreadLocal对象。
获取线程变量get
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程的ThreadLocalMap 如果为空初始化ThreadLocalMap 并赋值为当前值为空
ThreadLocalMap map = getMap(t);
if (map != null) {
//通过当前ThreadLocal对象获取Entry数组下标获取Entry对象
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
-------------------------------map.getEntry(this)-------------------------------------------------
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
清除remove()
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
//遍历整个数组清除所有与当前线程和ThreadLocal对象相关的数据
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
//判断数组下标对应referet对象和当前ThreadLocal对象是否同一个,若是清除
if (e.get() == key) {
e.clear();
expungeStaleEntry(i);
return;
}
}
}
谈一谈ThreadLocal扩容机制:在set到entry[]数组中会进行key过期处理,当清理过后数组长度大于len2/3进行一个rehash操作,在扩容方法之前再次进行一次key清理,清理过后如果数组长度>=len2/3 - len2/31/4=len/2两步判断最终是否需要扩容
InheritableThreadLocal源码分析
InheritableThreadLocal继承了ThreadLocal重写了childValue,getMap,createMap三个方法,InheritableThreadLocal与ThreadLocal区别在于,主线程设置的ThreadLocal在子线程中是无法获取到主线程ThreadLocal内的变量,而InheritableThreadLocal却可以获取的到
static ThreadLocal threadLocal = new ThreadLocal();
static InheritableThreadLocal tableLocal = new InheritableThreadLocal();
public static void main(String[] args) throws InterruptedException {
threadLocal.set("main Thread current value is ThreadLocal");
tableLocal.set("main Thread current value is InheritableThreadLocal");
new Thread(() -> {
System.out.println("child Thread get parent value :" + threadLocal.get());
}).start();
new Thread(() -> {
System.out.println("child Thread get parent value :" + tableLocal.get());
}).start();
}
child Thread get parent value :null
child Thread get parent value :main Thread current value is InheritableThreadLocal
区别在于new Thread线程会调用线程init方法,init方法有个判断如下:
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
父线程的Thread中inheritableThreadLocals是否为空inheritableThreadLocals也是ThreadLocal.ThreadLocalMap对象,如果不为空进行子线程的InheritableThreadLocal创建
//parentMap是父线程的ThreadLocalMap
private ThreadLocalMap(ThreadLocalMap parentMap) {
//父线程的entry[]数组
Entry[] parentTable = parentMap.table;
//父线程的entry[]数组长度
int len = parentTable.length;
//设置子线程的加载因子
setThreshold(len);
//创建与父线程entry数组一样长度的Entry数组
table = new Entry[len];
//遍历将父线程的值copy到子线程的Entry数组中
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) {
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++;
}
}
}
}