用法
ThreadLocal<String> threadLocal = new ThreadLocal<>(); // 無初始值
ThreadLocal<String> threadLocal = ThreadLocal.withInitial(() -> "123"); // 有初始值
threadLocal.set("123"); // set操作
threadLocal.get(); // get操作
threadLocal.remove(); // remove操作
一個小例子:
public static void main(String[] args) throws InterruptedException {
ThreadLocal threadLocal = new InheritableThreadLocal();
threadLocal.set("Hello");
System.out.println("現線上程是" + Thread.currentThread().getName() + ", 嘗試獲取:" + threadLocal.get());
new Thread(() -> {
threadLocal.set("World");
System.out.println("現線上程是" + Thread.currentThread().getName() + ", 嘗試獲取:" + threadLocal.get());
threadLocal.remove();
}).start();
Thread.sleep(3000);
System.out.println("現線上程是" + Thread.currentThread().getName() + ", 嘗試獲取:" + threadLocal.get());
threadLocal.remove();
}
輸出:
現線上程是main, 嘗試獲取:Hello
現線上程是Thread-0, 嘗試獲取:World
現線上程是main, 嘗試獲取:Hello
實現
set操作
public void set(T value) {
Thread t = Thread.currentThread(); // 獲取當前執行緒
ThreadLocalMap map = getMap(t); // 獲取ThreadLocalMap
if (map != null)
map.set(this, value);
else
createMap(t, value); // 建立map
}
// getMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
public class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals = null;
}
// createMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY]; // INITIAL_CAPACITY = 16;
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); // 計算下標
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY); // 設定閾值
}
// 雜湊值
private final int threadLocalHashCode = nextHashCode();
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT); // HASH_INCREMENT = 0x61c88647;
}
// ThreadLocalMap資料結構
static class ThreadLocalMap {
private Entry[] table;
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1); // 下標
// 使用線性探測法來解決雜湊衝突
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get(); // 獲取弱引用ThreadLocal
if (k == key) { // 對於已經存在的key,直接賦值
e.value = value;
return;
}
if (k == null) { // 弱引用ThreadLocal為null,進行替換
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value); // 上面兩種情況不是,直接賦值
int sz = ++size; // size+1
if (!cleanSomeSlots(i, sz) && sz >= threshold) // 清除無效entry,大於閾值,擴容並重新雜湊化
rehash();
}
get操作
public T get() {
Thread t = Thread.currentThread(); // 獲取當前執行緒
ThreadLocalMap map = getMap(t); // 獲取ThreadLocalMap
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this); // 傳入自己,也就是threadlocal物件,得到entry
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1); // 獲得下標
Entry e = table[i]; // 取值
if (e != null && e.get() == key) // 正好取到,直接返回
return e;
else
return getEntryAfterMiss(key, i, e);
}
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) { // 依次加一往後取值
ThreadLocal<?> k = e.get();
if (k == key) // 取到返回
return e;
if (k == null) // 弱引用ThreadLocal為null,清除無效的entry
expungeStaleEntry(i); //
else
i = nextIndex(i, len); // 下一個
e = tab[i];
}
return null;
}
remove操作
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
e.clear(); // 弱引用的引用置為null
expungeStaleEntry(i); // 清除entry,並重新雜湊化
return;
}
}
}
public void clear() {
this.referent = null;
}
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length;
// 槽置為null
tab[staleSlot].value = null;
tab[staleSlot] = null;
size--;
// 後面的進行重新雜湊化
Entry e;
int i;
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get();
if (k == null) {
e.value = null;
tab[i] = null;
size--;
} else {
int h = k.threadLocalHashCode & (len - 1);
if (h != i) {
tab[i] = null;
while (tab[h] != null)
h = nextIndex(h, len);
tab[h] = e;
}
}
}
return i;
}
記憶體洩露
當ThreadLocal沒有強依賴,ThreadLocal會在下一次發生GC時被回收,key是被回收了,但是value卻沒有被回收,為了防止這個問題出現,最好手動呼叫remove方法。