Looper
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
Looper
中創(chuàng)建了一個ThreadLocal<T> T
為Looper
的變量
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
在?prepare
?方法中硼补,調(diào)用了?sThreadLocal.set()?
和?sThreadLocal.get()?
兩個方法霎终,會在下面展開
其中?new Looper(quitAllowed)
?是調(diào)用了構(gòu)造函數(shù)
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
很顯然,在任意線程調(diào)用国旷,創(chuàng)建的?Looper
?就會持有該線程的引用。
同時我們看到,還有另一個方法能創(chuàng)建調(diào)用了?prepare
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
在這個方法中陶缺,先是通過?prepare?
方法,調(diào)用?sMainLooper?的?sThreadLocal.set()
?方法洁灵。
- 接著通過下面這個?
myLooper()?
方法獲取饱岸,這時候這個?threadLocal?
又出現(xiàn)了,我們再次放一放徽千。
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
看到這兒可能會有疑問苫费,注釋中說的是返回與當(dāng)前線程有關(guān)聯(lián)的?Looper?
,并不是返回主線程的?Looper?
双抽,那為何?prepareMainLooper?
方法是直接調(diào)用這個方法呢百框?
我們回到?prepareMainLooper()?
方法,它的注釋是這樣說的
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
也就是說荠诬,這個方法只會在應(yīng)用創(chuàng)建時被程序自己調(diào)用琅翻,不應(yīng)該被我們調(diào)用,這也確保了當(dāng)前是處在主線程中柑贞。
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
這個靜態(tài)方法方椎,方便了我們在程序中,可以快速的拿到主線程Looper
钧嘶。
ThreadLocal
說到現(xiàn)在棠众,我們已經(jīng)把?Looper?
中和?ThreadLocal?
相關(guān)的部分都講完了,接下來有决,就直接跳轉(zhuǎn)到?ThreadLocal?
類中闸拿,看看?set?
和?get?
分別是什么。
set
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
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();
}
前兩行代碼都是相同的书幕,獲取當(dāng)前線程的ThreadLocalMap
變量新荤。
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
查看Thread
代碼,發(fā)現(xiàn)這個?threadLocals?
是?ThreadLocal.ThreadLocalMap?
類型的變量台汇。
?ThreadLocalMap?
是定義在?ThreadLocal?
中的內(nèi)部類
/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the map
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//構(gòu)造函數(shù)
/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
?createMap?
中調(diào)用了構(gòu)造函數(shù)苛骨,參數(shù)為?this
?和?firstValue
?
我們發(fā)現(xiàn),對于Looper
來說苟呐,第一個參數(shù)實(shí)際上是?ThreadLocal<Looper>?
痒芝,而第二個參數(shù)是這個范型對應(yīng)的具體對象。
每個線程都有?ThreadLocalMap
?對象牵素,其中的對應(yīng)一個hash表?Entry[]
?严衬,存儲了不同范型對象的鍵值對。
原理總結(jié)
線程共享變量緩存如下:
Thread.ThreadLocalMap<ThreadLocal, Object>
;
Thread: 當(dāng)前線程笆呆,可以通過
Thread.currentThread()
獲取请琳。ThreadLocal:我們的static ThreadLocal變量粱挡。
Object: 當(dāng)前線程共享變量。
我們調(diào)用ThreadLocal.get
方法時单起,實(shí)際上是從當(dāng)前線程中獲取ThreadLocalMap<ThreadLocal, Object>抱怔,然后根據(jù)當(dāng)前ThreadLocal
獲取當(dāng)前線程共享變量Object。
ThreadLocal.set
嘀倒,ThreadLocal.remove
實(shí)際上是同樣的道理屈留。
這種存儲結(jié)構(gòu)的好處:
線程死去的時候,線程共享變量
ThreadLocalMap
則銷毀测蘑。ThreadLocalMap<ThreadLocal,Object>
鍵值對數(shù)量為ThreadLocal
的數(shù)量灌危,一般來說ThreadLocal
數(shù)量很少,相比在ThreadLocal
中用Map<Thread, Object>
鍵值對存儲線程共享變量(Thread數(shù)量一般來說比ThreadLocal數(shù)量多)碳胳,性能提高很多勇蝙。
內(nèi)存泄漏問題
其中?Entry?類的定義如下
/**
* 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;
}
}
ThreadLocalMap<ThreadLocal, Object>弱引用問題:
當(dāng)線程沒有結(jié)束,但是ThreadLocal
已經(jīng)被回收挨约,則可能導(dǎo)致線程中存ThreadLocalMap<null, Object>的鍵值對味混,造成內(nèi)存泄露。
(ThreadLocal
被回收诫惭,ThreadLocal
關(guān)聯(lián)的線程共享變量還存在)翁锡。
雖然ThreadLocal的get,set方法可以清除ThreadLocalMap
中key為null的value夕土,但是get馆衔,set方法在內(nèi)存泄露后并不會必然調(diào)用,所以為了防止此類情況的出現(xiàn)怨绣,我們有兩種手段角溃。
1、使用完線程共享變量后篮撑,顯式調(diào)用ThreadLocalMap.remove
方法清除線程共享變量减细;
2、JDK建議ThreadLocal
定義為private static
赢笨,這樣ThreadLocal
的弱引用問題則不存在了未蝌。