ThreadLocal Thread ThreadLocalMap 之間的關係
- 2020 年 6 月 1 日
- 筆記
ThreadLocal
:每個執行緒通過此對象都會返回各自的值,互不干擾,這是因為每個執行緒都存著自己的一份副本。需要注意的是執行緒結束後,它所保存的所有副本都將進行垃圾回收(除非存在對這些副本的其他引用)
ThreadLocal
的get
操作是這樣執行的:ThreadLocalMap map = thread.threadLocals
-> return map.getEntry(threadLocal)
ThreadLocal
的set
操作是這樣執行的:ThreadLocalMap map = thread.threadLocals
-> map.set(threadLocal, value)
三者的關係是:
- 每個
Thread
對應的所有ThreadLocal
副本都存放在ThreadLocalMap
對象中,key
是ThreadLocal
,value
是副本數據 ThreadLocalMap
對象存放在Thread
對象中- 通過
ThreadLocal
獲取副本數據時,實際是通過訪問Thread
來獲取ThreadLocalMap
,再通過ThreadLocalMap
獲取副本數據
示例程式碼如下:
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
/**
* @author: lihui
* @date: 2020-06-01
*/
public class ThreadLocalStudy {
private static final ThreadLocal<String> threadLocal1 = new ThreadLocal<>();
private static final ThreadLocal<String> threadLocal2 = new ThreadLocal<>();
private static CountDownLatch countDownLatch1 = new CountDownLatch(2);
private static CountDownLatch countDownLatch2 = new CountDownLatch(1);
public static void main(String[] args)
throws NoSuchFieldException, IllegalAccessException, InterruptedException {
Thread thread1 = new Thread(() -> {
threadLocal1.set("thread1-local1");
threadLocal2.set("thread1-local2");
countDownLatch1.countDown();
try {
countDownLatch2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread thread2 = new Thread(() -> {
threadLocal1.set("thread2-local1");
threadLocal2.set("thread2-local2");
countDownLatch1.countDown();
try {
countDownLatch2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();
thread2.start();
countDownLatch1.await();
System.out.println(threadLocal1 + " " + threadLocal2);
printThreadLocalMapInfo(thread1);
printThreadLocalMapInfo(thread2);
countDownLatch2.countDown();
}
/**
* 輸出相關資訊
*/
private static void printThreadLocalMapInfo(Thread thread) throws NoSuchFieldException, IllegalAccessException {
System.out.println("=====" + thread.getName() + "=====");
Object threadLocalMapObject = getThreadLocalMapObject(thread);
System.out.println(threadLocalMapObject);
List<Object> objects = getEntryList(threadLocalMapObject);
for (Object object : objects) {
System.out.println(getEntryKey(object) + " " + getEntryValue(object));
}
}
/**
* 獲取ThreadLocalMap對象
*/
private static Object getThreadLocalMapObject(Thread thread) throws NoSuchFieldException, IllegalAccessException {
Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
threadLocalsField.setAccessible(true);
return threadLocalsField.get(thread);
}
/**
* 獲取ThreadLocalMap對象中的所有Entry
*/
private static List<Object> getEntryList(Object threadLocalMapObject)
throws NoSuchFieldException, IllegalAccessException {
Field tableField = threadLocalMapObject.getClass().getDeclaredField("table");
tableField.setAccessible(true);
Object[] objects = (Object[]) tableField.get(threadLocalMapObject);
return Arrays.stream(objects).filter((obj) -> {
return obj != null;
}).collect(Collectors.toList());
}
/**
* 獲取Entry的key
*/
private static Object getEntryKey(Object entry) throws NoSuchFieldException, IllegalAccessException {
Field referentField = entry.getClass().getSuperclass().getSuperclass().getDeclaredField("referent");
referentField.setAccessible(true);
return referentField.get(entry);
}
/**
* 獲取Entry的value
*/
private static Object getEntryValue(Object entry) throws NoSuchFieldException, IllegalAccessException {
Field valueField = entry.getClass().getDeclaredField("value");
valueField.setAccessible(true);
return valueField.get(entry);
}
}
輸出結果為:
java.lang.ThreadLocal@31221be2 java.lang.ThreadLocal@377dca04
=====Thread-0=====
java.lang.ThreadLocal$ThreadLocalMap@728938a9
java.lang.ThreadLocal@377dca04 thread1-local2
java.lang.ThreadLocal@31221be2 thread1-local1
=====Thread-1=====
java.lang.ThreadLocal$ThreadLocalMap@25f38edc
java.lang.ThreadLocal@377dca04 thread2-local2
java.lang.ThreadLocal@31221be2 thread2-local1
可以看出:Thread類裡面的ThreadLocalMap存儲著所有ThreadLocal的副本數據。
沒有通過ThreadLocal的get方式進行獲取數據,而是通過實實在在的通過ThreadLocalMap對象來觀察數據。