理解TheadLocal源码
记忆会被改变,以前看过的代码,现在发现和记忆中的不一样,大脑会修改记忆的确是真的. 那就重复的看下,记录下吧.
作用: 为每个使用该变量的线程创建一个独立的变量副本.在多线程环境,就能避免变量被篡改问题. 重要的方法:
protected T initialValue() ;
public T get();
public void set(T value);
public void remove();
我们在程序中使用方式:
ThreadLocal<Integer> threadLocal = new ThreadLocal<>();//or ThreadLocal.withInitial(() -> 1);
threadLocal.set(2);
设置值的分析:
//分割 -----
//TheadLocal中无参构造方法是个空方法.见下
public ThreadLocal() {
}
//TheadLocal中的set方法.
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);//map不为空 直接放入值
else
createMap(t, value);//创建ThreadLocalMap 并设置第一个值
}
//首次ThreadLocalMap为空, 创建ThreadLocalMap 并设置第一个值
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];//INITIAL_CAPACITY=16
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);//设置扩容的阀值
}
//若是ThreadLocalMap不为空, 在数组中放置值的过程, 1.相同key 覆盖,2.key为null 替换,3.创建新Entry
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 == key) {
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();//扩容.
}
取值的分析:
//ThreadLocal 取值,1.获取线程对应的ThreadLocalMap 2.map取对应的Entry
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();
}
//从ThreadLocalMap中取对应Entry
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);//存在hash冲突时,需要遍历取
}
//由于hash冲突等 首次取值失败,遍历取值.
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;
}
删除值–匹配删除.
private void remove(ThreadLocal<?> 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) {//只有key为同一个才删除
e.clear();
expungeStaleEntry(i);
return;
}
}
}
总结: ThreadLocal 其实是一个线程级的Entry数组(即:每个线程里都有一个ThreadLocal.ThreadLocalMap类型的threadLocals变量),每次设置值或取值时,先拿到当前线程的这个变量.然后对变量(Entry数组)进行操作. 说的再通俗一点就是: ThreadLocal 是属于线程的变量,定义在Thread中的.所以是线程私有的. ps:(ThreadLocalMap)Entry数组是存放线程级的变量的地方,使用数组存储就需要考虑hash冲突的问题,这里处理hash冲突跟HashMap是不同的,HashMap是链地址法,ThreadLocalMap是开放定址法-线性探测再散列.