
ThreadLocal在android中屬于一個(gè)常用的類,比如的Android最重要的Handler消息機(jī)制里面的Looper存儲(chǔ)也是采用ThreadLocal,開源框架EventBus存儲(chǔ)當(dāng)前線程下的發(fā)送事件隊(duì)列狀態(tài)也是采用ThreadLocal。
ThreadLocal到底能做什么呢?原理又是什么呢?
點(diǎn)進(jìn)ThreadLocal的類文件看一下,類上寫的注釋。
This class provides thread-local variables. These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* {@code get} or {@code set} method) has its own, independently initialized
* copy of the variable. {@code ThreadLocal} instances are typically private
* static fields in classes that wish to associate state with a thread (e.g.,
* a user ID or Transaction ID).
*
* <p>For example, the class below generates unique identifiers local to each
* thread.
* A thread's id is assigned the first time it invokes {@code ThreadId.get()}
* and remains unchanged on subsequent calls.
* <pre>
* import java.util.concurrent.atomic.AtomicInteger;
*
* public class ThreadId {
* // Atomic integer containing the next thread ID to be assigned
* private static final AtomicInteger nextId = new AtomicInteger(0);
*
* // Thread local variable containing each thread's ID
* private static final ThreadLocal<Integer> threadId =
* new ThreadLocal<Integer>() {
* @Override protected Integer initialValue() {
* return nextId.getAndIncrement();
* }
* };
*
* // Returns the current thread's unique ID, assigning it if necessary
* public static int get() {
* return threadId.get();
* }
* }
* </pre>
* <p>Each thread holds an implicit reference to its copy of a thread-local
* variable as long as the thread is alive and the {@code ThreadLocal}
* instance is accessible; after a thread goes away, all of its copies of
* thread-local instances are subject to garbage collection (unless other
* references to these copies exist).
大體意思就是ThreadLocal 實(shí)現(xiàn)一個(gè)線程本地的存儲(chǔ)。每個(gè)線程都有自己的局部變量。所有線程都共享一個(gè)ThreadLocal對(duì)象,但是每個(gè)線程在訪問這些變量的時(shí)候能得到不同的值,每個(gè)線程可以更改這些變量并且不會(huì)影響其他的線程,并且支持null值。
我們看下ThreadLocal的方法有哪些?
- void set(Object value)設(shè)置當(dāng)前線程的線程局部變量的值。
- public Object get()該方法返回當(dāng)前線程所對(duì)應(yīng)的線程局部變量。
- public void remove()將當(dāng)前線程局部變量的值刪除,目的是為了減少內(nèi)存的占用,該方法是JDK 5.0新增的方法。需要指出的是,當(dāng)線程結(jié)束后,對(duì)應(yīng)該線程的局部變量將自動(dòng)被垃圾回收,所以顯式調(diào)用該方法清除線程的局部變量并不是必須的操作,但它可以加快內(nèi)存回收的速度。
- protected Object initialValue()返回該線程局部變量的初始值,該方法是一個(gè)protected的方法,顯然是為了讓子類覆蓋而設(shè)計(jì)的。這個(gè)方法是一個(gè)延遲調(diào)用方法,在線程第1次調(diào)用get()或set(Object)時(shí)才執(zhí)行,并且僅執(zhí)行1次。ThreadLocal中的缺省實(shí)現(xiàn)直接返回一個(gè)null。
我們使用一下ThreadLocal
創(chuàng)建一個(gè)ThreadLocal,存入一段文本,看在兩個(gè)線程取出值是多少。
local = new ThreadLocal<>();
local.set("存入測(cè)試");
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
Log.e(TAG, "t1 get(): " + local.get());
local.set("t1");
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
Log.e(TAG, "t2 get() " + local.get());
}
});
t1.start();
t2.start();
Log.e(TAG, "main get(): "+local.get() );

我們發(fā)現(xiàn)在t1 t2中取出值都是null,那么我們?cè)趖1 t2中存儲(chǔ)再取出驗(yàn)證下
local = new ThreadLocal<>();
local.set("存入測(cè)試");
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
local.set("t1");
Log.e(TAG, "t1 get(): " + local.get());
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
local.set("t2");
Log.e(TAG, "t2 get() " + local.get());
}
});
t1.start();
t2.start();
Log.e(TAG, "main get(): "+local.get() );

獲取大家好奇,怎么就在拿不到local的值呢,我們上面說到ThreadLocal是每個(gè)線程可以訪問自己內(nèi)部的副本變量。
我們看下ThreadLocal是怎么實(shí)現(xiàn)這種神奇機(jī)制的的?
先看下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);
}
我們不去關(guān)注細(xì)節(jié),大家有時(shí)間可以仔細(xì)看下源碼,我們獲取了一個(gè)map,進(jìn)行了判null處理,將值存進(jìn)去。如果是null就去創(chuàng)建map并將值賦進(jìn)去??梢岳斫鉃閗ey就是當(dāng)前的線程。
再看一下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();
}
根據(jù)線程,取出一個(gè)map,并根據(jù)類型取出一個(gè)Entry,根據(jù)泛型強(qiáng)轉(zhuǎn),并返回,如果沒有的話,我們看到一個(gè)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;
}
可以看到如果沒查到值initialValue,會(huì)默認(rèn)返回一個(gè)null。
其實(shí)ThreadLocalMap的源碼大家可以看一下,因?yàn)榇a量較多,我就不粘出來了。我們剛看到的Entry其實(shí)是一個(gè)WeakReference。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
最后看下remove()方法
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
其實(shí)也就是根據(jù)當(dāng)前線程判斷,刪除this,這里的this指向的就是ThreadLocal自己。
其實(shí)還有很多細(xì)節(jié),大家可以研究下源碼的時(shí)候看一下,想一下
這篇文章寫的不錯(cuò),大家可以看看。
https://www.cnblogs.com/xzwblog/p/7227509.html
