set方法
当创建一个ThreadLocal对象执行set方法时,首先会获取到当前线程的threadLocalMaps.
public void set(T value) {
Thread t = Thread.currentThread();
// 获得当前线程的ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}
调用map.set()
// 设置threadLocalMap中Entry[] 的值
插入时分为4种情况
1. 待插入的下标,是空位置直接插入。
2. 待插入的下标,不为空,key 相同,直接更新
3. 待插入的下标,不为空,key 不相同,开放寻址
4. 不为空,key 不相同,碰到过期key。其实情况4,遇到的是弱引用发生GC时,产生的情况。碰到这种情况,ThreadLocal 会进行探测清理过期key
每次插入之后会进行一次探测性清理来判断是否满足扩容的条件也就是
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
cleanSomeSlots代码
说白了就是判断entry中是否有需要清楚的对象。
private boolean cleanSomeSlots(int i, int n) {
boolean removed = false;
Entry[] tab = table;
int len = tab.length;
do {
i = nextIndex(i, len);
Entry e = tab[i];
if (e != null && e.get() == null) {
n = len;
removed = true;
i = expungeStaleEntry(i);
}
} while ( (n >>>= 1) != 0);
return removed;
}
expungeStaleEntry
就是进行清理。
resize()代码
rehash首先进行探测性清理然后进行resize()操作,resize判断当前的数组是否需要扩容,如果需要将Entry的容量扩展为之前的2倍
private void rehash() {
expungeStaleEntries();
// Use lower threshold for doubling to avoid hysteresis
if (size >= threshold - threshold / 4)
resize();
}
get方法
首先就是获取当前线程对应的map然后获取当前threadLocal对象.
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
getEntry方法
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);
}
getEntryAfterMiss方法
会进行判断当前元素是否被GC回收了,如果k == null 进行清楚.
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;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}