ThreadLocal,可以理解爲線程本地變量。通過ThreadLocal保存的值在同一線程中是數據共享的,而在不同線程中是互斥的(所謂互斥這裏可以理解爲同一個ThreadLocal變量在不同線程中值是不同的,在一個線程中改變ThreadLocal的值也不會影響另外一個線程中的值)。
ThreadLocal實例演示
首先,聲明一個ThreadLocal的變量,然後在UI線程,子線程1中設置對應的值,在子線程2中不設置值。要想給ThreadLocal變臉設置值,只要調用set方法即可,並通過get方法可以獲取對應的值。下面通過一個簡單demo演示下ThreadLocal的用法。
代碼如下:
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread() {
@Override
public void run() {
// 1、在子線程1中設置值
mThreadLocalStr.set("thread1");
System.out.println(Thread.currentThread() + " " + mThreadLocalStr.get());
};
}.start();
new Thread() {
@Override
public void run() {
System.out.println(Thread.currentThread() + " " + mThreadLocalStr.get());
};
}.start();
// 3、在主線程中設置值
mThreadLocalStr.set("main");
System.out.println(Thread.currentThread() + " " + mThreadLocalStr.get());
}
/**
* 申明一個字符串型ThreadLocal變量
*/
public ThreadLocal<String> mThreadLocalStr = new ThreadLocal<String>();
}
日誌:
02-15 20:24:00.885: I/System.out(2225): Thread[main,5,main] main
02-15 20:24:00.885: I/System.out(2225): Thread[Thread-143,5,main] thread1
02-15 20:24:00.886: I/System.out(2225): Thread[Thread-144,5,main] null
通過日誌打印結果我們可知:在UI線程中和子線程中分別打印了線程中的值,而在Thread2中因爲沒有設置ThreadLocal,所以爲null,這也印證了上面所提到的ThreadLocal在不同線程中值是獨立的。
ThreadLocal源碼分析
ThreadLocal方法列表:
- set():將此線程局部變量的當前線程副本中的值設置爲指定值
- get(): 返回此線程局部變量的當前線程副本中的值,如果沒有設置值就會返回initialValue()方法中指定的值
- initialValue():返回此線程局部變量的當前線程的“初始值”
- remove(): 移除此線程局部變量當前線程的值
通過ThreadLocal源碼可知,ThreadLocal是一個泛型類,通過泛型方法set可以設置ThreadLocal的值,通過get可以獲取ThreadLocal的值。
在ThreadLocal中還定義了一個內部靜態類Values,該類是存儲ThreadLocal變量的具體實現,在該類中定義了一個數組,就是用於存儲線程以及ThreadLocal變量。
public class ThreadLocal<T> {
public ThreadLocal() {}
/**
*
* 獲取當前線程的ThreadLocal的值
*/
@SuppressWarnings("unchecked")
public T get() {
// 獲取當前線程的values對象
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
// 計算存儲key所在的index,然後獲取key對應的值,在存儲的時候也是通過hash的方式計算index
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
// 如果沒有存儲,那麼會初始化current.localValues = new Values();
values = initializeValues(currentThread);
}
// 如果沒有設置值,會返回initialValue()中的值作爲默認值
return (T) values.getAfterMiss(this);
}
/**
* 設置默認值
*/
protected T initialValue() {
return null;
}
/**
* 設置當前線程的ThreadLocal的值
*/
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
/**
* 移除線程本地變臉
*/
public void remove() {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
values.remove(this);
}
}
/**
* 初始化
*/
Values initializeValues(Thread current) {
return current.localValues = new Values();
}
/**
* 在Thread類中定義了一個Values對象localValues,用於存儲本線程所有的ThreadLocal變量
*/
Values values(Thread current) {
return current.localValues;
}
private final Reference<ThreadLocal<T>> reference
= new WeakReference<ThreadLocal<T>>(this);
private static AtomicInteger hashCounter = new AtomicInteger(0);
private final int hash = hashCounter.getAndAdd(0x61c88647 * 2);
/**
*
* 內部類,實現存儲ThreadLocal的key-value的數據結構
* 該類通過數組實現,key和value分別被存放在index、index+1的位置
*
* 因爲一個Thread可能有多個不同的ThreadLocal變量,所在在計算key的index時,通過hash運算保證唯一
*/
static class Values {
/**
* 將key-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表示線程中存儲的本地變量的數量
size++;
return;
}
// Remember first tombstone.
if (firstTombstone == -1 && k == TOMBSTONE) {
firstTombstone = index;
}
}
}
/**
* 如果沒有設置線程的ThreadLocal值,那麼返回默認值並且存儲key-value值
*/
Object getAfterMiss(ThreadLocal<?> key) {
Object[] table = this.table;
int index = key.hash & mask;
if (table[index] == null) {
// 獲取默認值
Object value = key.initialValue();
if (this.table == table && table[index] == null) {
// 存儲默認值
table[index] = key.reference;
table[index + 1] = value;
size++;
cleanUp();
return value;
}
// The table changed during initialValue().
put(key, value);
return value;
}
Object reference = table[index];
if (reference == key.reference) {
return table[index + 1];
}
// If no entry was found...
if (reference == null) {
Object value = key.initialValue();
// If the table is still the same...
if (this.table == table) {
// If we passed a tombstone and that slot still
// contains a tombstone...
if (firstTombstone > -1
&& table[firstTombstone] == TOMBSTONE) {
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
// No need to clean up here. We aren't filling
// in a null slot.
return value;
}
// If this slot is still empty...
if (table[index] == null) {
table[index] = key.reference;
table[index + 1] = value;
size++;
cleanUp();
return value;
}
}
// The table changed during initialValue().
put(key, value);
return value;
}
if (firstTombstone == -1 && reference == TOMBSTONE) {
// Keep track of this tombstone so we can overwrite it.
firstTombstone = index;
}
}
}
/**
* 移除key-value
*/
void remove(ThreadLocal<?> key) {
cleanUp();
for (int index = key.hash & mask;; index = next(index)) {
Object reference = table[index];
if (reference == key.reference) {
// Success!
table[index] = TOMBSTONE;
table[index + 1] = null;
tombstones++;
size--;
return;
}
if (reference == null) {
// No entry found.
return;
}
}
}
/**
* 清楚已經被GC清除的ThreadLocal變量
*/
private void cleanUp() {
if (rehash()) {
// If we rehashed, we needn't clean up (clean up happens as
// a side effect).
return;
}
if (size == 0) {
// No live entries == nothing to clean.
return;
}
// Clean log(table.length) entries picking up where we left off
// last time.
int index = clean;
Object[] table = this.table;
for (int counter = table.length; counter > 0; counter >>= 1,
index = next(index)) {
Object k = table[index];
if (k == TOMBSTONE || k == null) {
continue; // on to next entry
}
// The table can only contain null, tombstones and references.
@SuppressWarnings("unchecked")
Reference<ThreadLocal<?>> reference
= (Reference<ThreadLocal<?>>) k;
if (reference.get() == null) {
// This thread local was reclaimed by the garbage collector.
table[index] = TOMBSTONE;
table[index + 1] = null;
tombstones++;
size--;
}
}
// Point cursor to next index.
clean = index;
}
}
}