ThreadLocal說(shuō)明
ThreadLocal是一個(gè)線程內(nèi)部的數(shù)據(jù)存儲(chǔ)類,使用它來(lái)保存數(shù)據(jù)干厚,只有當(dāng)前的線程才可以訪問(wèn)螃宙,其他線程無(wú)法訪問(wèn)到其存儲(chǔ)的數(shù)據(jù)谆扎,這個(gè)在某些場(chǎng)景下是非常有用的堂湖。比如:Android 里面Looper无蜂,Handler機(jī)制,對(duì)于Handler來(lái)說(shuō)训桶,要獲取到線程里面的Looper舵揭,就必須使用ThreadLocal來(lái)存儲(chǔ)躁锡,否則無(wú)法拿到指定的Looper午绳,這個(gè)在源碼中也有所體現(xiàn):
//1 Handler.java
public Handler(Callback callback, boolean async) {
...略去
//獲取looper
mLooper = Looper.myLooper();
...略去
}
//2 Looper.java
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
...略去
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//存儲(chǔ)
sThreadLocal.set(new Looper(quitAllowed));
}
public static Looper myLooper() {
//獲取存儲(chǔ)的Looper
return sThreadLocal.get();
}
ThreadLocal 使用小demo
public class MainActivity extends AppCompatActivity {
@BindView(R.id.btn_text)
Button mBtnText;
private ThreadLocal<Boolean> mBooleanThreadLocal = new ThreadLocal<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
Logger.init("liao").logLevel(LogLevel.FULL);
}
@OnClick(R.id.btn_text)
public void textClick() {
mBooleanThreadLocal.set(true);
Logger.d("[Thread#main]mBooleanThreadLocal= %s", mBooleanThreadLocal.get());
new Thread("Thread#1") {
@Override
public void run() {
mBooleanThreadLocal.set(false);
Logger.d("[Thread#1]mBooleanThreadLocal= %s", mBooleanThreadLocal.get());
}
}.start();
new Thread("Thread#2") {
@Override
public void run() {
Logger.d("[Thread#2]mBooleanThreadLocal= %s", mBooleanThreadLocal.get());
}
}.start();
}
}
輸出結(jié)果:
可以看到,
第一個(gè)在主線程設(shè)置為true稚铣,所以獲取到的為true箱叁;
第二個(gè)我們?cè)O(shè)置了為false墅垮,所以獲取到的為false;
第三個(gè)我們沒(méi)有賦值耕漱,所以獲取到的為null算色。
從上面的日志我們可以看出,雖然我們?cè)L問(wèn)的是同一個(gè)對(duì)象螟够,但是獲取到的值是不一樣的灾梦,這個(gè)就是ThreadLocal的神奇之處,在某些場(chǎng)景下我們可以實(shí)現(xiàn)很復(fù)雜的功能。
ThreadLocal 原理
ThreadLocal是一個(gè)泛型類:
public class ThreadLocal<T>
既然具有存儲(chǔ)數(shù)據(jù)的功能,那么就會(huì)有g(shù)et鲫忍,set等方法,所以我們只需弄懂這些方法射亏,那么ThreadLocal的原理也就明白了永品。
首先我們看下Thread類:
//1. Thread.java
public class Thread implements Runnable {
...
//專門用于存儲(chǔ)線程的ThreadLocal的數(shù)據(jù)
ThreadLocal.Values localValues;
...
}
再來(lái)看下ThreadLocal類的set方法:
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
//為null,則初始化一個(gè)Values值
values = initializeValues(currentThread);
}
//存儲(chǔ)ThreadLocal值
values.put(this, value);
}
下面我們來(lái)看Values類,此類提供存儲(chǔ)ThreadLocal值的設(shè)置:
static class Values {
...
//存儲(chǔ)ThreadLocal值
private Object[] table;
...
// 具體存儲(chǔ)ThreadLocal算法:
//ThreadLocal的值在table數(shù)組中的存儲(chǔ)位置總是為
//ThreadLocal的reference字段所標(biāo)識(shí)的對(duì)象的下一個(gè)位置,
//比如ThreadLocal的reference對(duì)象在table數(shù)組的索引為
//index涉瘾,那么ThreadLocal的值在table數(shù)組中的索引就是index+1。
//所以最終ThreadLocal的值將會(huì)被存儲(chǔ)在table數(shù)組中:table[index + 1] = value其做。
void put(ThreadLocal<?> key, Object value) {
cleanUp();
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];
if (k == key.reference) {
// Replace existing entry.
table[index + 1] = value;
return;
}
if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;
size++;
return;
}
// Go back and replace first tombstone.
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
return;
}
// Remember first tombstone.
if (firstTombstone == -1 && k == TOMBSTONE) {
firstTombstone = index;
}
}
}
}
最后再來(lái)看下ThreaLocal的get方法:
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
//獲取存儲(chǔ)ThreadLocal數(shù)據(jù)值
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
//獲取值ThreadLoad索引的下一個(gè)
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
最后附上一張類圖: