ThreadLocal是什么
ThreadLocal一般稱為線程本地變量,它是一種特殊的線程綁定機制,將變量與線程綁定在一起,為每一個線程維護一個獨立的變量副本。通過ThreadLocal可以將對象的可見范圍限制在同一個線程內(nèi)。
ThreadLocal用法和原理
ThreadLocal提供了4個公共的方法
1、ThreadLocal.get: 獲取ThreadLocal中當(dāng)前線程共享變量的值。
2、ThreadLocal.set: 設(shè)置ThreadLocal中當(dāng)前線程共享變量的值。
3、ThreadLocal.remove: 移除ThreadLocal中當(dāng)前線程共享變量的值。
4、ThreadLocal.initialValue: ThreadLocal沒有被當(dāng)前線程賦值時或當(dāng)前線程調(diào)用remove方法后調(diào)用get方法,返回此方法值。
我們通過Handler的源碼來分析下ThreadLocal的使用和原理,上代碼:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
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));
}
在handler中的上述兩個方法中,使用了ThreadLocal的get和set方法,這也就是為什么能夠保證一個線程中始終只有一個Looper對象,這樣防止了數(shù)據(jù)的臟讀,即防止當(dāng)前線程訪問到其他線程的數(shù)據(jù)。我們先從set方法的源碼看起
public void set(T value) {
Thread t = Thread.currentThread();
//通過當(dāng)前線程獲取ThreadLocalMap ,
ThreadLocalMap map = getMap(t);
if (map != null)
//map的key是ThreadLocal,value為Looper
map.set(this, value);
else
//map為null時創(chuàng)建map
createMap(t, value);
}
先來看看getMap()方法做了什么
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
上述代碼可以看出,在Thread中維護了一個 ThreadLocal.ThreadLocalMap對象
再來看createMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
上述代碼不需解釋,接下來看看get方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
//在set方法中我們看到,map的key是ThreadLocal,所以這里通過ThreadLocal來獲取map中的value,這行代碼可以看出Looper被包裝在了ThreadLocalMap.Entry中
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
//Entry中的value即為Looper
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
如果map為null,看這里setInitialValue
private T setInitialValue() {
//這里即是ThreadLocal沒有被當(dāng)前線程賦值時或當(dāng)前線程調(diào)用remove方法后調(diào)用initialValue
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
這里總結(jié)一下Thread,ThreadLocal和ThreadLocalMap三者之間的關(guān)系
一個Thread中只有一個ThreadLocalMap,一個ThreadLocalMap中可以有多個ThreadLocal對象(ThreadLocal為map的key),其中一個ThreadLocal對象對應(yīng)一個ThreadLocalMap中的一個Entry。
接下來我們看下ThreadLocalMap.Entry
static class ThreadLocalMap {
/**
* 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;
}
}
我們看到Entry是靜態(tài)內(nèi)部類,這樣的好處的是防止持有外部類的引用而引起的內(nèi)存泄漏,接著Entry 繼承了WeakReference,即弱引用對象,將map的key即ThreadLocal對象變成一個弱引用的對象
最后我們看看ThreadLocalMap底層是什么數(shù)據(jù)結(jié)構(gòu)
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//是一個初始長度為16的Entry數(shù)組
table = new Entry[INITIAL_CAPACITY];
//自己實現(xiàn)了如何從 key 到 value 的映射
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
使用一個 static 的原子屬性 AtomicInteger nextHashCode,通過每次增加 HASH_INCREMENT = 0x61c88647 ,然后 & (INITIAL_CAPACITY - 1) 取得在數(shù)組 private Entry[] table 中的索引。
public class ThreadLocal<T> {
/**
* ThreadLocals rely on per-thread linear-probe hash maps attached
* to each thread (Thread.threadLocals and
* inheritableThreadLocals). The ThreadLocal objects act as keys,
* searched via threadLocalHashCode. This is a custom hash code
* (useful only within ThreadLocalMaps) that eliminates collisions
* in the common case where consecutively constructed ThreadLocals
* are used by the same threads, while remaining well-behaved in
* less common cases.
*/
private final int threadLocalHashCode = nextHashCode();
/**
* The next hash code to be given out. Updated atomically. Starts at
* zero.
*/
private static AtomicInteger nextHashCode =
new AtomicInteger();
/**
* The difference between successively generated hash codes - turns
* implicit sequential thread-local IDs into near-optimally spread
* multiplicative hash values for power-of-two-sized tables.
*/
private static final int HASH_INCREMENT = 0x61c88647;
/**
* Returns the next hash code.
*/
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
上述key至value的映射作者也不甚明了,簡單說來ThreadLocalMap是一個類似HashMap的集合,只不過自己實現(xiàn)了尋址,也沒有HashMap中的put方法,而是set方法等區(qū)別。