一、背景
ThreadLocal類顧名思義就是,申明為ThreadLocal的變量,對于不同線程來說都是獨立的。
下面是一個例子:
public class Test {
public static void main(String[] args) {
ThreadLocalTest threadLocalTest = new ThreadLocalTest();
for(int i = ; i < ; i++) {
TaskTest taskTest = new TaskTest(threadLocalTest);
Thread t = new Thread(taskTest);
t.start();
try {
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class TaskTest implements Runnable{
ThreadLocalTest test;
public TaskTest(ThreadLocalTest test) {
this.test = test;
}
@Override
public void run() {
int tmp = test.localCnt.get();
test.localCnt.set(tmp + );
test.shareCnt += ;
System.out.println(Thread.currentThread().getName());
System.out.println("LocalCnt:" + test.localCnt.get());
System.out.println("SharedCnt:" + test.shareCnt);
}
}
static class ThreadLocalTest
{
ThreadLocal<Integer> localCnt = new ThreadLocal<Integer>() {
public Integer initialValue() {
return ;
}
};
int shareCnt = ;
public ThreadLocalTest() {
}
}
}
輸出結果:
Thread-
LocalCnt:
SharedCnt:
Thread-
LocalCnt:
SharedCnt:
Thread-
LocalCnt:
SharedCnt:
基本原理:ThreadLocal會為每一個線程提供一個獨立的變量副本,進而隔離了多個線程對資料的通路沖突。因為每一個線程都擁有自己的變量副本,進而也就沒有必要對該變量進行同步了。ThreadLocal提供了線程安全的共享對象,在編寫多線程代碼時,可以把不安全的變量封裝進ThreadLocal。
二、存儲結構
在ThreadLocal類中定義了一個重要靜态内部類,ThreadLocalMap,用來存儲每個線程的局部變量,代碼如下
static class ThreadLocalMap {
// Entry繼承自WeakReference類,是存儲線程私有變量的資料結構
// ThreadLocal執行個體作為引用,意味着如果ThreadLocal執行個體為null
// 就可以從table中删除對應的Entry。
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
// 把ThreadLocal與value封裝成Entry
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
// 數組初始大小為16
private static final int INITIAL_CAPACITY = ;
// 存儲數組
private Entry[] table;
private int size = ;
private int threshold; // 預設為0
private void setThreshold(int len) {
threshold = len * / ;
}
private static int nextIndex(int i, int len) {
return ((i + < len) ? i + : );
}
private static int prevIndex(int i, int len) {
return ((i - >= ) ? i - : len - );
}
// 構造函數
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - );
table[i] = new Entry(firstKey, firstValue);
size = ;
setThreshold(INITIAL_CAPACITY);
}
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (int j = ; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - );
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
}
三、主要方法
public class ThreadLocal<T> {
// 跟hash值相關的部分
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode =
new AtomicInteger();
private static final int HASH_INCREMENT = ;
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
// 此方法在每個線程中最多執行一次,如果第一次執行get(),會調用此方法
// 如果在第一次執行get()之前已經調用過set(),則此方法永遠不執行
// 可以看到預設傳回null值,為了避免不必要錯誤,最好重寫此方法
protected T initialValue() {
return null;
}
// 構造函數
public ThreadLocal() {
}
// 擷取線程所屬的值
public T get() {
// 擷取目前線程
Thread t = Thread.currentThread();
// 每個線程有維護一個ThreadLocalMap變量,調用getMap擷取
ThreadLocalMap map = getMap(t);
// 如果map不為空
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
// 如果map中已經有該ThreadLocal的值,傳回
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// 在沒有map或map中沒有添加該ThreadLocal時調用初始化
return setInitialValue();
}
// 初始化
private T setInitialValue() {
// 調用initialValue擷取預設值
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
// 如果Thread中并沒有map,則建立一個
// 這裡注意,是每個Thread維護一個ThreadLocalMap
createMap(t, value);
return value;
}
// 指派
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
// 這裡同樣有可能調用建立ThreadLocalMap
createMap(t, value);
}
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
// 傳回Thread中維護的TreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
// 如果Thread中并沒有map,則建立一個
// 這裡注意,是每個Thread維護一個ThreadLocalMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
}
四、總結
ThreadLocal類最重要的一個概念是,其原理是通過一個ThreadLocal的靜态内部類ThreadLocalMap實作,但是實際中,ThreadLocal不儲存ThreadLocalMap,而是有每個Thread内部維護
ThreadLocal.ThreadLocalMap threadLocals
一份資料結構。
這裡畫張圖更容易了解,假如我們有如下的代碼
class ThreadLocalDemo
{
ThreadLocal<Integer> localA = new ThreadLocal<Integer>();
ThreadLocal<Integer> localB = new ThreadLocal<Integer>();
}
在多線程環境下,資料結構應該是如下圖所示
