1、前言
- 最初看到
ThreadLocal這個東西是在Handler消息機制的Looper實例化的時候, 系統(tǒng)把Looper的實例對象保存在ThreadLocal里,當(dāng)有需要的時候就直接拿出來用,以此保證一個線程只有一個Looper對象; - 這篇文章就是對
ThreadLocal源碼進行解析,記錄其工作過程與原理;
2、定義
先來看看系統(tǒng)源碼對其的一個簡單描述:
/**
* Implements a thread-local storage, that is, a variable for which each thread
* has its own value. All threads share the same {@code ThreadLocal} object,
* but each sees a different value when accessing it, and changes made by one
* thread do not affect the other threads. The implementation supports
* {@code null} values.
*
* @see java.lang.Thread
* @author Bob Lee
*/
public class ThreadLocal<T> {
···
···
}
觀察最上面的注釋,大概的意思是ThreadLocal是每個線程的成員變量,實現(xiàn)線程本地的存儲,所有線程都共享同一個對象,但是訪問的時候卻具有不同的值,實際就是在不同的線程中提供一份各自的副本,這樣線程與線程之間的數(shù)據(jù)就不會相互影響,是獨立存在的;
這句話是什么意思呢,舉個栗子:
public class MainActivity extends AppCompatActivity {
private ThreadLocal<String> mThreadLocal = new ThreadLocal<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mThreadLocal.set("A");
Log.i("測試","result->"+mThreadLocal.get());
new Thread(new Runnable() {
@Override
public void run() {
mThreadLocal.set("B");
Log.i("測試","result->"+mThreadLocal.get());
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
Log.i("測試","result->"+mThreadLocal.get());
}
}).start();
}
首先我們定義了一個全局的ThreadLocal對象,然后分別在三個不同的線程中存入不同的值,然后得出的結(jié)果是:
07-08 14:01:38.149 19251-19251/com.qinkl.demo I/測試: result->A
07-08 14:01:38.151 19251-19294/com.qinkl.demo I/測試: result->B
07-08 14:01:38.168 19251-19295/com.qinkl.demo I/測試: result->null
在不同的線程中全局變量ThreadLocal的值是都是不同的,通過這個例子很好的證明了系統(tǒng)源碼的描述:多個線程共享同一個變量,但是訪問的值是不同的,只有一個解釋,就是在不同的線程中產(chǎn)生了各自的副本,在進行數(shù)據(jù)操作的時候,是互相獨立的,線程與線程之間不會有影響;
要知道ThreadLocal為什么會產(chǎn)生這樣的效果,就要從源碼入手,下面我們就開始探究其原理是怎樣的。
3、源碼解析
首先我們從ThreadLocal的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);
}
首先是獲得當(dāng)前所在的Thread對象,然后作為參數(shù)傳入getMap方法,在getMap方法中,返回的是當(dāng)前線程的以ThreadLocal.ThreadLocalMap聲明的threadLocals變量,判斷如果這個值不為空的話就直接存入ThreadLocalMap中,如果為空的話就調(diào)用createMap方法,該方法實例化了一個ThreadLocalMap對象并且賦值給線程的threadLocals變量,在構(gòu)造函數(shù)里面保存這個值;
ThreadLocalMap是什么呢:
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;
}
}
/**
* The initial capacity -- MUST be a power of two.
*/
private static final int INITIAL_CAPACITY = 16;
/**
* The table, resized as necessary.
* table.length MUST always be a power of two.
*/
private Entry[] table;
···
}
ThreadLocalMap對象是ThreadLocal的靜態(tài)內(nèi)部類,內(nèi)部通過一個Entry[]數(shù)組存儲數(shù)據(jù),并且該數(shù)組的key為ThreadLocal的弱引用,設(shè)計的目的是防止Entry[]持有外部類ThreadLocal引用導(dǎo)致不能回收的內(nèi)存泄露;
每個線程都持有一個自己的ThreadLocalMap,也就是持有自己的獨立副本,數(shù)據(jù)的操作都是在
ThreadLocalMap的數(shù)組完成的,所以保證了線程與線程之間不會有影響;
然后再看看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) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
protected T initialValue() {
return null;
}
在get方法中,主要還是判斷當(dāng)前線程是否存在ThreadLocalMap對象,然后從Entry[]數(shù)組中取出該ThreadLocal對應(yīng)的value值,如果沒有的話,在setInitialValue方法中,設(shè)置為null的初始值;
4、總結(jié)
- 雖然每個線程都共享同一對象,但是會在線程各自創(chuàng)建自己的副本,該副本是
ThreadLocalMap對象; -
ThreadLocal本質(zhì)是操作線程中ThreadLocalMap對象,而不是其本身; -
ThreadLocalMap對象內(nèi)部維護了Entry[]數(shù)組存儲數(shù)據(jù),并且key為弱引用,目的是防止內(nèi)存泄漏;