這個東東大家可能平時會聽說過,但是了解可能不是很多,當然網上也有了很多介紹文章,這里我只按照我的理解來說一下。知道這個東西應該挺多都是看 Looper 看到的。
我們先看看這個東西都是怎么用的:
private final Handler uiThreadHandler = new Handler(Looper.getMainLooper());
ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
int mainThreadNum = 1;int workerThreadNum = 2;
public void testThreadLocal() {
threadLocal.set(mainThreadNum);
new Thread(new Runnable() {
@Override
public void run() {
threadLocal.set(workerThreadNum);
uiThreadHandler.post(new Runnable() {
@Override
public void run() {
Log.d("UIThread", "" + threadLocal.get());
}
});
Log.d("WorkerThread", "" + threadLocal.get());
}
}).start();
Log.d("UIThread", "" + threadLocal.get());
}
這里的輸出
UIThread: 1
WorkerThread: 2
UIThread: 1
可見,ThreadLocal 的功能就是在不同 Thread set 進去的數據不會相互干擾并且可以直接獲取到。
然后我們說一下這個東西是怎么實現的:
public class ThreadLocal<T> {
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode = new AtomicInteger();
public ThreadLocal() { }
...
static class ThreadLocalMap {
...
}
}
不管別的,先看構造函數與成員變量。由上可知,可以隨意構造實例,并且成員變量只有一個 threadLocalHashCode,所以很明顯的可以知道這貨就是個皮包公司,它并不存儲任何實例,只是包含了一個這個實例的索引,也就是threadLocalHashCode。
那接下來我們在看看它的成員函數,這里只那 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);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
這里我們可能就比較清晰為什么 ThreadLocal 可以提供這種可以在不同線程存入/讀取數據而互不干擾的功能了。
就是因為實際的變量并不是存入到了 ThreadLocal 中,而是存入到了這個操作對應的 Thread.threadLocals 中了(可以看上邊的 getMap 函數)。而且這個 Thread.threadLocals 的實例化也是在 ThreadLocal 的代碼中(createMap 函數)。
這個變量在 Thread 中的定義:
public class Thread implements Runnable {
...
ThreadLocal.ThreadLocalMap threadLocals = null;
...
}
就是一個引用而已,那現在就指向了 ThreadLocalMap 這個數據結構。
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal> {
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
...
}}
這里邊最主要的其實就是這個數組 Entry[] table,這個 Entry 的定義也在上邊了,其實就是一個包含了具體變量的 ThreadLocal 的弱引用。
而 threadLocalHashCode 這個變量就是計算具體變量在這個數組中的索引的。
ok,整個的東西已經串下來了。
當然,這里邊疑問還有很多,比如 threadLocalHashCode 這個具體的計算方式,這個 table 數組是如何擴容等問題,大家可以自行看代碼了。