核心提煉
-
類有維護了一個屬性變量 Thread
(ThreadLocal.ThreadLocalMap threadLocals = null),也就是說每個線程有都一個自己的 threadLocals
,是以每個線程往這個 ThreadLocalMap
中讀寫隔離的,并且是互相不會影響的 。ThreadLocal
-
類是 ThreadLocalMap
的靜态内部類ThreadLocal
-
維護了一個 ThreadLocalMap
數組,Entry
的 key 是 Entry
對象,value 是存入的對象,是以一個 ThreadLocal
隻能存儲一個Object對象,如果需要存儲多個Object對象那麼就需要多個 ThreadLocal
ThreadLocal
-
的 key 引用 Entry
是弱引用ThreadLocal
-
、Thread
ThreadLocalMap
總覽圖如下ThreadLocal

ThreadLocal 是用來幹嘛的
ThreadLocal 主要是用在多線程的場景中
- 儲存線程上下文資訊,在任意需要的地方可以擷取 (比如下面案例中的儲存使用者資訊)
- 線程安全,避免某些情況需要考慮線程安全必須同步帶來的性能損失
使用場景案例
在spring MVC開發中,我們常用 ThreadLocal 儲存目前登陸使用者資訊,這樣線程在任意地方都可以取到使用者資訊,比如我們會有以下類似下面的
UserContext
類,然後給配置一個攔截器,攔截器裡面在請求執行前調用
UserContext
的
setUserInfo
方法将使用者資訊存入
ThreadLocal
對象
userInfoLocal
中,然後在請求的具體執行的任意地方調用
UserContext
getUserInfo
方法取出使用者資訊,最後在攔截器裡面在請求結束傳回前調用
UserContext
clear
方法清除資料
public class UserContext {
private static final ThreadLocal<UserInfo> userInfoLocal = new ThreadLocal<UserInfo>();
public static UserInfo getUserInfo() {
return userInfoLocal.get();
}
public static void setUserInfo(UserInfo userInfo) {
userInfoLocal.set(userInfo);
}
public static void clear() {
userInfoLocal.remove();
}
}
ThreadLocal 使用代碼示例
public class ThreadLocalTest {
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
threadLocal.set(i);
System.out.println(Thread.currentThread().getName() + "====" + threadLocal.get());
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
threadLocal.remove();
}
}, "thread-1").start();
new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "====" + threadLocal.get());
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
threadLocal.remove();
}
}, "thread-2").start();
}
}
運作結果
thread-1====0
thread-2====null
thread-2====null
thread-1====1
thread-2====null
thread-1====2
thread-2====null
thread-1====3
thread-2====null
thread-1====4
從運作結果可以看出,
thread-1
線程中對
threadLocal
對象的指派對
thread-2
線程中
threadLocal
對象的值任何影響
源碼細節
Thread 類
public class Thread implements Runnable {
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
}
Thread
類有屬性變量
threadLocals
(類型是ThreadLocal.ThreadLocalMap),也就是說每個線程有一個自己的
ThreadLocalMap
,是以每個線程往這個
ThreadLocal
中讀寫隔離的,并且是互相不會影響的
一個ThreadLocal隻能存儲一個Object對象,如果需要存儲多個Object對象那麼就需要多個ThreadLocal
ThreadLocal 類
類簽名
public class ThreadLocal<T> {
}
關鍵方法 | set
public void set(T value) {
Thread t = Thread.currentThread();
// ** 取出目前線程維護的 ThreadLocalMap 對象 **
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
// 建立一個 ThreadLocalMap 指派為目前 Thread 對象的屬性,并添加第一個 Entry
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
關鍵方法 | get
public T get() {
Thread t = Thread.currentThread();
// ** 取出目前線程維護的 ThreadLocalMap 對象 **
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();
}
// 取出線程維護的 ThreadLocalMap 對象
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
其他方法
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
ThreadLocalMap 類
ThreadLocalMap
是
ThreadLocal
的靜态内部類
public class ThreadLocal<T> {
// ThreadLocalMap 是 ThreadLocal 的靜态内部類
static class ThreadLocalMap {
// Entry 是 ThreadLocalMap 的靜态内部類
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
// Entry 的 key 是 ThreadLocal 對象,value 是關聯的資料對象
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
// ThreadLocalMap 維護了一個 Entry 數組,
private Entry[] table;
}
}
構造方法
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
// 如果 ThreadLocal 在 Entry 數組中已經存在,則替換其 value
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;
}
}
// 不存在,則建立一個 Entry 插入到 Entry數組中
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
關鍵方法 | getEntry
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);
}
/**
* Remove the entry for key.
*/
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();
expungeStaleEntry(i);
return;
}
}
}
ThreadLocalMap 裡 Entry 為何聲明為 WeakReference?
// Entry 是 ThreadLocalMap 的靜态内部類
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
WeakReference是什麼
- 強引用(StrongReference):被強引用關聯的對象不會被回收
- 軟引用(SoftReference):被軟引用關聯的對象隻有在記憶體不夠的情況下才會被回收
- 弱引用(WeakReference):被弱引用關聯的對象一定會被回收,也就是說它隻能存活到下一次垃圾回收發生之前
WeakReference 是 Java 語言規範中為了差別直接的對象引用(程式中通過構造函數聲明出來的對象引用)而定義的另外一種引用關系。WeakReference 标志性的特點是:reference 執行個體不會影響到被引用對象的 GC 回收行為(即隻要對象被除 WeakReference 對象之外所有的對象解除引用後,該對象便可以被 GC 回收),隻不過在被對象回收之後,reference 執行個體想獲得被應用的對象時程式會傳回 null。
為什麼ThreadLocalMap的key用弱引用,為什麼不用強引用呢?
看回這個例子
public class UserContext {
private static final ThreadLocal<UserInfo> userInfoLocal = new ThreadLocal<UserInfo>();
public static UserInfo getUserInfo() {
return userInfoLocal.get();
}
public static void setUserInfo(UserInfo userInfo) {
userInfoLocal.set(userInfo);
}
public static void clear() {
userInfoLocal.remove();
}
}
此時 Entry 的情況是
key instance of WeakReference<ThreadLocal<UserInfo>>
value instance of UserInfo
WeakReference 對引用的對象 userInfoLocal 是弱引用,不會影響到 userInfoLocal 的 GC 行為。 如果是強引用的話,線上程運作過程中,我們不再使用 userInfoLocal 了,将 userInfoLocal 置為 null,但 userInfoLocal 線上程的 ThreadLocalMap 裡還有引用,導緻其無法被GC回收(當然,可以等到線程運作結束後,整個Map都會被回收,但很多線程要運作很久,如果等到線程結束,便會一直占着記憶體空間)。 而 Entry 聲明為 WeakReference,userInfoLocal 置為 null 後,線程的 threadLocalMap 就不算強引用了,userInfoLocal 就可以被GC回收了。map的後續操作中,也會逐漸把對應的"stale entry"清理出去,避免記憶體洩漏。