我們在談Handler機制的時候,其實也就是談Handler、Message、Looper、MessageQueue之間的關系,對于其工作原理我們不做詳解(Handler機制詳解)。
- Message:Handler發送、接收和處理的消息對象
- Looper:每個線程隻能擁有一個Looper.它的looper()方法負責循環讀取MessageQueue中的消息并将讀取到的消息交給發送該消息的handler進行處理。
- MessageQueue:消息隊列,它采用先進先出的方式來管理Message。程式在建立Looper對象時,會在它的構造器中建立MessageQueue。
Handler類簡析
Handler類的主要作用有兩個:在新啟動的線程中發送消息;在主線程中擷取和處理消息。
而要完整的了解Handler機制,對于Looper的底層存儲和輪詢機制是必須了解的,看過了其實就很簡單,今天就專門講這個。
ThreadLocal詳解
為了友善大家了解,我們直接看源碼:
public class ThreadLocal<T> {
.....
}
- 1
- 2
- 3
這裡可以看出threadlocal是一個範型類,這标志着threadlocal可以存儲所有資料,作為存儲資料來說,我們首先想到的是會對外提供set(),get(),remove(),等方法。
set()方法:
/**
* Sets the value of this variable for the current thread. If set to
* {@code null}, the value will be set to null and the underlying entry will
* still be present.
*
* @param value the new value of the variable for the caller thread.
*/
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
從源碼可以看出,首先擷取目前線程,然後調用values方法,我們來看下values方法:
/**
* Gets Values instance for this thread and variable type.
*/
Values values(Thread current) {
return current.localValues;
}
- 1
- 2
- 3
- 4
- 5
- 6
該方法是傳回目前線程的一個存儲實類,那ThreadLocal又是什麼呢?上面說過 ThreadLocal是一個線程内部的資料存儲類,通過它可以在指定的線程中存儲資料,資料存儲以後,隻有在指定線程中可以擷取到存儲的資料。
我們來看幾個ThreadLocal方法,先回到set方法,得到values的實類以後會來一個判斷,為null調用initializeValues(currentThread)
Values initializeValues(Thread current) {
return current.localValues = new Values();
}
- 1
- 2
- 3
接下來調用value的put方法,我們想到的應該是往裡面插值,也就是我們說的put()。
void put(ThreadLocal<?> key, Object value) {
cleanUp();
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -;
for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];
if (k == key.reference) {
// Replace existing entry.
table[index + ] = value;
return;
}
if (k == null) {
if (firstTombstone == -) {
// Fill in null slot.
table[index] = key.reference;
table[index + ] = value;
size++;
return;
}
// Go back and replace first tombstone.
table[firstTombstone] = key.reference;
table[firstTombstone + ] = value;
tombstones--;
size++;
return;
}
// Remember first tombstone.
if (firstTombstone == - && k == TOMBSTONE) {
firstTombstone = index;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
從源碼可以看出,把values的值傳入到一個table數組的key.reference的下一個下标中,至此,我們了解了Threadlocal的存值過程,首先會擷取目前線程,根據目前線程擷取Values存儲類,該存儲類在該線程是單例的,在調用values存儲類中的put方法,最終将存儲的内容存儲到Values内部類的table數組下标為key.reference中 。
接下來我們來看一下取值的方法:
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + ];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
ThreadLocal的get方法的邏輯也比較清晰,它同樣是取出目前線程的localValues對象,如果這個對象為null那麼就傳回初始值,初始值由ThreadLocal的initialValue方法來描述。
protected T initialValue() {
return null;
}
- 1
- 2
- 3
如果localValues對象不為null,那就取出它的table數組并找出ThreadLocal的reference對象在table數組中的位置,然後table數組中的下一個位置所存儲的資料就是ThreadLocal的值。
接着我們再來看一下remove的方法實作:
void remove(ThreadLocal<?> key) {
cleanUp();
for (int index = key.hash & mask;; index = next(index)) {
Object reference = table[index];
if (reference == key.reference) {
// Success!
table[index] = TOMBSTONE;
table[index + ] = null;
tombstones++;
size--;
return;
}
if (reference == null) {
// No entry found.
return;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
到此我們就完全明白了ThreadLocal的存取原理了:通過ThreadLocal的set和get方法可以看出,它們所操作的對象都是目前線程的localValues對象的table數組,是以在不同線程中通路同一個ThreadLocal的set和get方法,它們對ThreadLocal所做的讀寫操作僅限于各自線程的内部,這就是為什麼ThreadLocal可以在多個線程中互不幹擾地存儲和修改資料。