ThreadLocal源码阅读笔记
JDK上对于ThreadLocal的说明是“该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。”由此可见,ThreadLocal保存的是线程自己变量,其他线程是不可见的。
在ThreadLocal的实现中,定义了一个嵌套类ThreadLocalMap。该类也定义了一个嵌套类Entry,用以存放线程的一个局部变量。ThreadLocalMap定义了一个Entry数组,用以存放线程的多个局部变量。每个局部变量都以ThreadLocal对象为键,T类型对象为值(T为ThreadLocalMap<T>中的T类型)。而在Thread类中定义了一个ThreadLocalMap类型的对象map,这些局部变量都放在这个对象内。
提供的方法:
T |
get() 返回此线程局部变量的当前线程副本中的值。 |
protected T |
initialValue() 返回此线程局部变量的当前线程的“初始值”。 |
void |
remove() 移除此线程局部变量当前线程的值。 |
void |
set(T value) 将此线程局部变量的当前线程副本中的值设置为指定值。 |
其中initialValue()用来设置初始值。当没有set()时就调用get()或者remove()之后没有set()就调用get()都会调用initialValue()进行初始化。当继承了ThreadLocal后可以覆盖该方法以指定不同的初值(默认为null)。
T get()调用了ThreadLocalMap的getEntry(ThreadLocal key)方法来寻找对应的值。
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);
}
//碰撞后采用线性探测法来处理,遇到老化了的Entry会调用expungeStaleEntry进行删除。
//而该函数不仅删除该Entry,还会将在这一块碰撞区域老化了的Entry删除,并调整没有老化的Entry的位置
//使得Entry更靠近它原本应该所在的位置,下一次探测时更快找到Entry。
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;
}
void remove()调用ThreadLocalMap的remove(ThreadLocal key)进行删除
//删除时是将该Entry的key设为null,并删除该Entry(同样是调用expungeStaleEntry进行删除)。
private void remove(ThreadLocalTest key) {
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)]) {
if (e.get() == key) {
e.clear();
expungeStaleEntry(i);
return;
}
}
}
void set(T value)是通过调用ThreadLocalMap中的set(ThreadLocal key, Object value)完成的
//如果map中有该键值对的Entry,则将其值设为value返回
//如果在线性探测的过程中遇到了老化的Entry则调用replaceStaleEntry(ThreadLocal key, Object value, int staleSlot)进行取代
//否则将包含该键值对的Entry加入到map中,如果超过了capacity(默认为table.length的2/3),需要进行重新配置map
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 == e.get()) {
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();
}
对于replaceStaleEntry(ThreadLocal key, Object value, int staleSlot),它会在当前碰撞快的staleSlot之后找是否有相应的键值对,如果有,设置相应的值,然后清理所有的老化的Entry;如果没有,则在staleSlot处放置Entry(key, value),然后也清理所有的老化的Entry。