今天看shiro相关的内容,读到了关于ThreadLocal的代码。感觉跟之前的理解有点差别,记录一下,先把看懂的记录一下。
ThreadLocal最常用的两个方法:set(T value), T get()
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
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();
}
首先看set方法:
1:首先获取当前线程 Thread t = Thread.currentThread();
2:然后调用getMap(t) 获取ThreadLocalMap,这个ThreadLocalMap直接从上面获取的线程中拿,代码如下:
ThreadLocalMap getMap(Thread t) { return t.threadLocals; // 注意,这里操作的是当前线程的ThreadLocalMap
}
3:然后判断后去的ThreadLocalMap是否为空,如果不为空,保存信息。set的时候第一个参数是自己,第二个参数是要保存的内容,所以最主要的还是看ThreadLocalMap的set方法,进入set方法:
private void set(ThreadLocal<?> key, Object value) {
/** 可以看到这里是用了一个Entry数组来保存数据,之前不明白这里为啥要用数组,来保存。后来想想一个线程有多个ThreadLocal,
每个ThreadLocal做set操作的时候都会操作的都是当前线程的ThreadLocalMap。并且set的时候用ThreadLocal的地址作为
key,保证当前线程中ThreadLocalMap的key不会重复
*/
Entry[] tab = table;
int len = tab.length;
// 这里用当前对象获取hashcode与数组长度做 与 运算,得出一个值,并且这个值不会超过数组的长度
int i = key.threadLocalHashCode & (len-1);
// 首先根据上面与操作获取的值去取数据,我认为是为了提高效率吧,也不知道对不对
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
// 如果key值相等,将原值覆盖
if (k == key) {
e.value = value;
return;
}
// TODO 下面这个暂时没看懂,看懂再更新
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
// 大意是上面的循环没有获取到相等的key,然后下面需要新增一个entry
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
get() 方法
public T get() {
// 首先还是获取当前线程
Thread t = Thread.currentThread();
// 返回当前线程的ThreadLocalMap 引用
ThreadLocalMap map = getMap(t);
if (map != null) {
// 调用ThreadLocalMap的getEntry方法获取entry
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
T result = (T)e.value;
return result;
}
}
//setInitialValue()方法,如果在使用ThreadLocal之前没有set值,直接调用get会返回null
return setInitialValue();
}
private Entry getEntry(ThreadLocal<?> key) {
// 当前key的hashcode与数组长度做 与 运算,得到一个下标
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
// entry不为空并且key相等的话,返回值。因为上面set值的时候,下标的获取也是和上面一样。我暂且认为是为了提高效率吧,毕竟 不用一个一个迭代了
if (e != null && e.get() == key)
return e;
else
// 如果为空或者key值不相等的话,迭代整个table
return getEntryAfterMiss(key, i, e);
}
到此还有两个方法没搞懂,搞懂了再来更新,哇咔咔:
get值的时候有个:
Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e)
set值的时候有个:
replaceStaleEntry(ThreadLocal<?> key, Object value, int staleSlot)