ThreadLocal
当使用ThreadLocal维护变量时,ThreadLocal为每个使用改变量的线程创建独立的变量副本;
每个线程可以操作属于自己的副本,不会对其他线程的副本产生干扰。以空间换时间的方式,使得访问并行化,对象共享化。
//get()方法:返回此ThreadLcoal变量的当前线程副本中的值
public T get() {
Thread t = Thread.currentThread();
//不论是get还是set操作,第一件事就是从Thread中找到相对应的ThreadLocalMap,即获取当前thread对应的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
T result = (T)e.value;
return result;
}}
return setInitialValue();
}
// getMap()
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
//ThreadLocalMap真正的引用是在Thread类中
ThreadLocal.ThreadLocalMap threadLocals = null;
###ThreadLocalMap
//ThreadLocalMap继承了WeakReference.ThreadLocalMap以ThreadLcoal这个弱引用作为key,那么在系统gc后,ThreadLcoal被回收,ThreadLocalMap中可能会存在key==null的value,如果创建ThreadLocal的线程一直在运行,且value无法访问,就会造成内存泄露。
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//仅以getEntry()方法来查看ThreadLocalMapMap内部的处理内存泄露的方法
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
//根据下标直接获取e的值
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal<?> k = e.get();
if (k == key)
return e;
//key==null时进行处理
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
//实际处理key==null的方法
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length;
// expunge entry at staleSlot
tab[staleSlot].value = null;
tab[staleSlot] = null;
size--;
// 再Hash处理
Entry e;
int i;
for (i = nextIndex(staleSlot, len);(e = tab[i]) != null;i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get();
//在key==null时,将value置为null,并且从table中删除
if (k == null) {
e.value = null;
tab[i] = null;
size--;
} else {
int h = k.threadLocalHashCode & (len - 1);
if (h != i) {
tab[i] = null;
while (tab[h] != null)
h = nextIndex(h, len);
tab[h] = e;
}
}
}
return i;
}
Hash冲突
//ThreadLocalMap解决Hash冲突采用的是线性探测法。根据初始key的hashcode值确定元素在table数组中的位置,如果发现这个位置上已经有其他key值的元素被占用,则利用固定的算法寻找一定步长的下个位置,依次判断,直至找到能够存放的位置。
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
private static int prevIndex(int i, int len) {
return ((i - 1 >= 0) ? i - 1 : len - 1);
}