Java線程變量ThreadLocal源碼分析
1.ThreadLocal 線程變量,和當(dāng)前線程綁定的,只保存當(dāng)前線程的變量,對于其他線程是隔離的,是訪問不到里面的數(shù)據(jù)的。
2.在Looper中使用到了ThreadLocal,創(chuàng)建了一個Looper是保存到了ThreadLocal中。
//這里用到了泛型,ThreadLocal中只保存Looper對象。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) { //保證Looper只被創(chuàng)建一次。
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}看下sThreadLocal.set()方法是如何保存數(shù)據(jù)的。
先拿到當(dāng)前線程,然后在拿到該線程的ThreadLocalMap成員變量,然后保存到這個map中,
key就是創(chuàng)建的ThreadLocal對象,value就是傳進來的value。
public void set(T value) {
//拿到當(dāng)前線程
Thread t = Thread.currentThread();
//得到一個map
ThreadLocalMap map = getMap(t);
if (map != null){
// 這個map是以當(dāng)前對象為key的,這個this就是 ThreadLocal的實例 sThreadLocal
map.set(this, value);
}else{
createMap(t, value);
}
}//getMap 是從Thread中拿到了一個threadLocals變量,是ThreadLocal.ThreadLocalMap 的實例。
//保存的數(shù)據(jù)也是存在了這個map中,這也就是為什么ThreadLocal是和線程綁定的,對其他線程來說是隔離的原因所在。
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}1)保存數(shù)據(jù),如果map不為空的情況。走上面if判斷的第一個分支。這個存儲方式和HashMap類似
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
// 計算出key在集合中的索引,index
int i = key.threadLocalHashCode & (len-1);
//開始遍歷整個數(shù)組,
//取出索引為i的Entry,如果不為空,取出下一個,進行遍歷
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
//如果取出的k和傳進來的key一致,則把新的值存起來。
if (k == key) {
e.value = value;
return;
}
//直到取出最有一個,k==null則進行存儲。
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
//如果索引i的位置,沒有Entry,則把傳進來的key和value保存在這個位置。
tab[i] = new Entry(key, value);
int sz = ++size;
//如果大于閾值了,則進行擴容
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}2)保存數(shù)據(jù),如果map為空的情況。則創(chuàng)建ThreadLocalMap,并賦值給當(dāng)前線程t。
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
} ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//創(chuàng)建一個大小為16的數(shù)組
table = new Entry[INITIAL_CAPACITY];
//計算得到的i是數(shù)組的角標(biāo)??梢詤⒖糷ashMap源碼分析
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//賦值,保存數(shù)據(jù)
table[i] = new Entry(firstKey, firstValue);
size = 1;
//擴容的閾值
setThreshold(INITIAL_CAPACITY);
}3.再看看ThreadLocal是如何取值的。
也是先拿到當(dāng)前線程t,然后通過t拿到他的成員變量ThreadLocalMap。
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//如果map不為空,則從map中取值
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//如果map為空
return setInitialValue();
}1)如果map不為空則從map中取值。
//如果map不為空
private Entry getEntry(ThreadLocal<?> key) {
//拿到key對應(yīng)的索引
int i = key.threadLocalHashCode & (table.length - 1);
//從數(shù)組中拿到Entry
Entry e = table[i];
if (e != null && e.get() == key){如果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;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}2)如果在get時,得到的map是空的,則這個時候需要初始化
//如果map為空,則調(diào)用這個方法,initialValue由用戶去實現(xiàn)。
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}//下面是Choreographer中的例子:
private static final ThreadLocal<Choreographer> sThreadInstance =
new ThreadLocal<Choreographer>() {
@Override
protected Choreographer initialValue() {
Looper looper = Looper.myLooper();
if (looper == null) {
throw new IllegalStateException("The current thread must have a looper!");
}
Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
if (looper == Looper.getMainLooper()) {
mMainInstance = choreographer;
}
return choreographer;
}
};總結(jié)ThreadLocal是通過 ThreadLocalMap 進行數(shù)據(jù)的存儲的。而這個ThreadLocalMap對象是通過
獲取到當(dāng)前線程,并從當(dāng)前線程中拿到的。所以ThreadLocalMap只保存本線程的數(shù)據(jù),做到了線程隔離。
ThreadLocalMap存數(shù)據(jù)的key是ThreadLocal對象本身。 map.set(this, value);
如果想要給ThreadLocalMap中存更多的數(shù)據(jù),則需要創(chuàng)建多個對象。
到此這篇關(guān)于Java線程變量ThreadLocal源碼分析的文章就介紹到這了,更多相關(guān)Java ThreadLocal內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot整合freemarker和相應(yīng)的語法詳解
FreeMarker是一款Spring官方推薦使用的模板引擎。接下來通過本文給大家介紹Springboot整合freemarker和相應(yīng)的語法,感興趣的朋友一起看看吧2021-09-09
Spring IOC源碼剖析_如何整體認(rèn)知Spring體系結(jié)構(gòu)
這篇文章主要介紹了Spring IOC源碼剖析_如何整體認(rèn)知Spring體系結(jié)構(gòu)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
關(guān)于Socket的解析以及雙方即時通訊的java實現(xiàn)方法
本篇文章主要介紹了關(guān)于Socket的解析以及雙方通訊的java實現(xiàn)方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-03-03

