一、ThreadLocal
一個類可以通過 ThreadLocal 在當前線程內部創建一個獨一無二的副本。通過threadLocal.get()
和threadLocal.set(inst)
可以獲取和設置當前線程中這個副本。
換句話說,一個ThreadLocal<T>
對象可以實現不同的線程對應不同的T類型對象。
如下例:
ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
threadLocal.set(1);
new Thread(() -> {
threadLocal.set(2);
System.out.println(threadLocal.get());
}).start();
Thread.sleep(10);
System.out.println(threadLocal.get());
以上代碼打印結果為:
2
1
ThreadLocal<T>
有兩個公開方法,get()
和set(T)
。
-
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) {
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
-
ThreadLocal.set(T)
:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocal.getMap(Thread)
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
可以看到,ThreadLocal的原理其實很簡單:在每個線程Thread
中,都有一個ThreadLocal.ThreadLocalMap
類型的變量;這是一個以ThreadLocal<T>
對象為key,以對應的T類型對象為value的map。也就是說,當使用ThreadLocal.set(T)
保存對象時,實際上存儲的地方是線程的內部。
雖然是在線程內部,key為ThreadLocal,value為T的map,但是也可以換個角度來看:
每一個ThreadLocal對象,都相當于保存了一個以線程Thread
為key,以對應類T的實例為value的map;而這個map是絕對線程安全的,并且規避了同步/加鎖的開銷。
要注意這是一個ThreadLocal對象。如果創建了多個ThreadLocal對象,那么每個對象對應的值可以是不同的。
ThreadLocal<Integer> t1 = new ThreadLocal<>();
ThreadLocal<Integer> t2 = new ThreadLocal<>();
t1.set(1);
t2.set(2);
System.out.println(t1.get() + ", " + t2.get());
輸出
1, 2
二、Android中的ThreadLocal與Looper
在Android中,之所以能夠實現每個線程對應一個Looper對象,其原理的就是使用了ThreadLocal。
在Looper中,有一個靜態成員sThreadLocal
:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
看Looper的prepare()
和myLooper()
方法:
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));
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
就是ThreadLocal的賦值與獲取過程。