??在學(xué)習(xí)Handler消息機(jī)制中Looper源碼時(shí)看到ThreadLocal這個(gè)類(lèi)逗概,發(fā)現(xiàn)它很強(qiáng)大并且很方便的實(shí)現(xiàn)了對(duì)各個(gè)線程中Looper的管理弟晚。這個(gè)類(lèi)的源碼只有600行。下面先上一個(gè)簡(jiǎn)單的例子:
public class ThreadLocalTest {
static ThreadLocal<Integer> intLocals = new ThreadLocal<Integer>(){
protected Integer initialValue() {
return 1;
}
};
public static void main(String[] args) {
intLocals.set(669);
new MyThread("A線程").start();
System.out.println(Thread.currentThread().getName() + "===="
+ intLocals.get());
}
static class MyThread extends Thread {
public MyThread(String name){
super(name);
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "===="
+ intLocals.get());
}
}
}
這段代碼的運(yùn)行結(jié)果:
main====669
A線程====1
提出問(wèn)題:用static關(guān)鍵字修飾的靜態(tài)變量intLocals
沒(méi)效果嗎逾苫,A線程中的輸出結(jié)果不應(yīng)該是669嗎卿城?
下面帶著這個(gè)疑問(wèn)去源碼中尋找答案:
ThreadLocal類(lèi)中只有一個(gè)空參的構(gòu)造方法,所以關(guān)鍵的代碼只有initLocals.set(669)
和initLocals.get()
了铅搓。
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);
}
/**
* @param t the current thread
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
從源碼中可以看出在Thread中定義了一個(gè)ThreadLocalMap的引用瑟押,如果該引用的對(duì)象不為null就會(huì)通過(guò)ThreadLocalMap的引用來(lái)調(diào)用set方法。ThreadLocalMap是ThreadLocal的一個(gè)靜態(tài)內(nèi)部類(lèi)星掰,而在ThreadLocalMap中還定義的一個(gè)靜態(tài)的Entry類(lèi)多望。以ThreadLocal作為鍵、Object為值的數(shù)據(jù)結(jié)構(gòu)氢烘;并將鍵存放到了WeakReference中怀偷,即線程中沒(méi)有ThreadLocal的其他引用時(shí)就會(huì)自動(dòng)回收。如果map為空就會(huì)執(zhí)行createMap()方法創(chuàng)建一個(gè)ThreadLocalMap對(duì)象播玖。
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
// 默認(rèn)的數(shù)組大小為16椎工,自定義的話(huà)必須為2的冪
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;
由此可見(jiàn)map.set(this, value)
只是將當(dāng)前的intLocals對(duì)象作為鍵,669作為值存儲(chǔ)到主線程中黎棠。
get方法源碼:
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
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();
}
get()通過(guò)獲取當(dāng)前線程的ThreadLocalMap對(duì)象map晋渺,遍歷map中數(shù)組拿到鍵為this(即當(dāng)前ThreadLocal的引用)的Entry實(shí)體,從而返回對(duì)應(yīng)的鍵脓斩;map為空時(shí)就返回初始化時(shí)的值木西。
??看了上面的源碼應(yīng)該能夠解答示例中的疑問(wèn)了:ThreadLocal中set(value)方法將調(diào)用這個(gè)方法的對(duì)象作為鍵、value為值存儲(chǔ)到當(dāng)前線程中ThreadLocalMap中随静;而get()方法取出時(shí)是根據(jù)調(diào)用這個(gè)方法的ThreadLocal對(duì)象到當(dāng)前線程的ThreadLocalMap中查找對(duì)應(yīng)的值八千,為空時(shí)就返回初始值吗讶。也就是說(shuō)ThreadLocal的get和set方法的操作對(duì)象其實(shí)都是執(zhí)行這兩個(gè)方法所在線程的ThreadLocalMap對(duì)象,ThreadLocal只起到了一個(gè)鍵的作用恋捆。
??ThreadLocal將對(duì)象的訪問(wèn)范圍限制在線程中照皆,并且當(dāng)線程結(jié)束后ThreadLocal會(huì)被自動(dòng)回收,也可以調(diào)用remove()(since 1.5)方法去掉線程中保存的變量沸停。
??另外膜毁,ThreadLocal不是為了解決線程間的同步問(wèn)題,感覺(jué)恰恰相反愤钾,它是為了避免產(chǎn)生同步問(wèn)題瘟滨。既然如此,為什么又要整出ThreadLocal這么個(gè)東西能颁,直接搞個(gè)局部變量不就好了嗎杂瘸?這個(gè)問(wèn)題暫時(shí)不知道咋回答,以后再補(bǔ)上;锞铡0苡瘛!
參考資料
Android7.0源碼
徹底理解ThreadLocal