我之前的一篇博客http://www.itdecent.cn/p/423544285f84,介紹了Android中的Service相關(guān)知識,其中有個點沒說,就是Service和IntentService的區(qū)別。接下來為了分析IntentService源碼,必須要先了解以下相關(guān)知識,HandlerThread、Handler、Looper、ThreadLocal等。今天這篇博客主要介紹ThreadLocal的使用以及實現(xiàn)原理。
ThreadLocal使用方法
在主線程中創(chuàng)建一個ThreadLocal對象,并通過set方法設(shè)置一個值,然后再創(chuàng)建一個子線程調(diào)用ThreadLocal對象set另外一個值,最后再創(chuàng)建一個子線程什么也不做,打印一下三個線程中的ThreadLocal對象get值。
ThreadLocal<Boolean> local = new ThreadLocal<>();
local.set(true);
Log.d(TAG,"main thread "+" threadlocal is "+local.get());
new Thread("Thread#1"){
@Override
public void run() {
local.set(false);
Log.d(TAG,getName()+" threadlocal is "+local.get());
super.run();
}
}.start();
new Thread("Thread#2"){
@Override
public void run() {
super.run();
Log.d(TAG,getName()+" threadlocal is "+local.get());
}
}.start();
輸出結(jié)果:
main thread threadlocal is true
Thread#1 threadlocal is false
Thread#2 threadlocal is null
從上面的日志中可以看出來,三個線程雖然都是調(diào)用同一個對象的get方法,但是結(jié)果卻完全不一樣,每個線程得到的值其實是他們所在線程的值,并不能讀取別的線程set的值。而且,當前線程set的值也不會影響到別的線程。非常神奇。
ThreadLocal源碼淺析
為了等下吃飯,寫作的動力立馬提高了不少,打字速度都提高了不少。
下面源碼是基于android-25分析的哦
上面的demo中總共就涉及到ThreadLocal的三個方法,一個構(gòu)造方法和set、get方法,構(gòu)造方法啥也沒有,來看set方法。
set
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
代碼很簡單哦,就是獲取調(diào)用改方法的線程,然后通過該線程獲取它的一個Map,如果Map為空,就createMap,不為空,就將需要設(shè)置的值設(shè)置到這個Map中了。
我們看一下createMap方法
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
從上面代碼可以發(fā)現(xiàn),這里為每個Thread都創(chuàng)建了一個成員變量threadLocals,這個threadLocals指向ThreadLocalMap,并且將ThreadLocal引用以及需要set的value值傳遞進去。
從這里應(yīng)該就可以看出來為什么可以做到多個線程可以不共享一個變量值了,因為每個線程都有一個自己的變量,那就是ThreadLocalMap,來存放自己的寶貝。
咱么調(diào)用ThreadLocal的set方法,其實就是調(diào)用ThreadLocal中的ThreadLocalMap這個內(nèi)部類的set方法,
我們接著看一下ThreadLocalMap,這個核心類哦。
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();
}
上面方法的參數(shù),key就是ThreadLocal引用,value就是需要設(shè)置的值。在ThreadLocalMap中有一個數(shù)組,用來存放<ThreadLocal,value>這個鍵值對,存放的位置就是通過int i = key.threadLocalHashCode & (len-1);來計算的,也就是根據(jù)ThreadLocal引用來計算在數(shù)組中的位置
考慮一下,這里為什么要弄了數(shù)組存放鍵值對?主要是因為一個線程可能有多個ThreadLocal對象了。
再來看一下get方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
很簡單啊,首先獲取到當前線程的成員變量ThreadLocalMap,然后根據(jù)ThreadLocal引用來獲取數(shù)組中存放的鍵值對,也就能獲取到當前線程之前保存的value值了。
關(guān)于這個ThreadLocal到底有什么作用,下回分解。