筆者日常: 需要學點前端的東西!從今天開始,以後發部落格按照二後一前的頻率進行發表!!!
聲明: 本文主要示範如何觀察及證明ThreadLocal,ThreadLocal原理會幾句話帶過。
ThreadLocal原理(簡述):
Thread類有一個threadLocals字段,該字段類型為ThreadLocal.ThreadLocalMap。簡單地說,threadLocals以ThreadLocal<T>執行個體對象的弱引用為key,以T為value,進行資料存儲。
- 當線上程A裡面調用ThreadLocal執行個體的set(T value)方法時,就(可以這麼了解)相當于一個map對象(一個線程對應一個這樣的map對象)以這個ThreadLocal對象的弱引用為key,以set的具體值為值,将其進行存儲。
- 當線上程A裡面調用ThreadLocal執行個體的get()方法時,就(可以這麼了解)相當于一個map對象(一個線程對應一個這樣的map對象)以這個ThreadLocal對象的弱引用為key,進行取值。
注:ThreadLocal的核心在于ThreadLocalMap,這裡本人不再進行相關知識介紹,感興趣的,可自行閱讀源
碼或查閱相關資料。這篇部落格講得比較細,可參考閱讀。
ThreadLocal資料觀察及驗證:
聲明: ThreadLocal核心為ThreadLocal$ThreadLocalMap,而ThreadLocalMap$ThreadLocalMap的
核心為table(該字段類型為ThreadLocal$ThreadLocalMap$Entry數組),下面主要觀察各個線程
的table資料資訊。
編寫輸出ThreadLocal$ThreadLocalMap$Entry資訊的工具類:
/**
* 獲得指定Thread的threadLocals屬性的table值
* 注:table的資料類型為java.lang.ThreadLocal$ThreadLocalMap$Entry[]
*
* @param targetThread
* 要擷取的線程
* @return 該線程對應的的threadLocals屬性的table值 的格式化輸出
* @date 2019/9/27 10:50
*/
private static String getThreadLocalMapTableInfo(Thread targetThread) {
// 先獲得class對象
Class threadClazz = targetThread.getClass();
Object threadLocalsValue;
Object tableValue;
try {
/// -> 從Thread中擷取到 目前線程的threadLocals(注:該字段類型為ThreadLocalMap)的值
// 獲得targetThread對應類的threadLocals屬性,并設定屬性可通路
Field threadLocalsField = threadClazz.getDeclaredField("threadLocals");
threadLocalsField.setAccessible(true);
// 從targetThread對象中,擷取threadLocals屬性的值
// 這裡即:擷取到Thread類的threadLocals屬性值
threadLocalsValue = threadLocalsField.get(targetThread);
/// -> 從ThreadLocalMap中擷取到tables數組(注:該字段類型為Entry[])的值
Class threadLocalMapClazz = threadLocalsValue.getClass();
// 獲得threadLocalMapClazz對應類的table屬性,并設定屬性可通路
Field tableField = threadLocalMapClazz.getDeclaredField("table");
tableField.setAccessible(true);
// 從threadLocalsValue對象中,擷取table屬性的值
// 這裡即:擷取到ThreadLocal$ThreadLocalMap類的table屬性值
tableValue = tableField.get(threadLocalsValue);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
return systemOutEntry(targetThread, (Object[])tableValue);
}
/**
* 優雅輸出java.lang.ThreadLocal$ThreadLocalMap$Entry[]執行個體的資訊
*/
private static String systemOutEntry(Thread targetThread, Object[] array) {
StringBuilder sb = new StringBuilder(64);
try {
/// -> 預處理java.lang.ThreadLocal$ThreadLocalMap$Entry的key
Class referenceClass = Class.forName("java.lang.ref.Reference");
Field keyField = referenceClass.getDeclaredField("referent");
keyField.setAccessible(true);
/// -> 預處理java.lang.ThreadLocal$ThreadLocalMap$Entry的value
Class entryClass = array.getClass().getComponentType();
Field valueField = entryClass.getDeclaredField("value");
valueField.setAccessible(true);
String key;
String value;
sb.append("線程");
sb.append(targetThread.getName());
sb.append("的threadLocals屬性值(即:ThreadLocal.ThreadLocalMap對象)的table(數組對象)的資訊為:");
sb.append("\n");
for (int i = 0; i < array.length; i++) {
value = array[i] == null ? null : String.valueOf(valueField.get(array[i]));
key = array[i] == null ? null : String.valueOf(keyField.get(array[i]));
sb.append("\t").append("index => ").append(i).append(", ");
sb.append("\t").append("{").append(key).append(": ").append(value).append("}");
sb.append("\n");
}
} catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
return sb.toString();
}
測試及說明:
- 運作以下測試方法:
/** 一個ThreadLocal */ private static final ThreadLocal<List<Integer>> TD_LIST = new ThreadLocal<>(); /** 另一個ThreadLocal */ private static final ThreadLocal<String> TD_STRING = new ThreadLocal<>(); /** * 主方法 * * @date 2019/9/26 15:07 */ @SuppressWarnings("all") public static void main(String[] args) throws InterruptedException, NoSuchFieldException, IllegalAccessException { System.out.println("對象的TD_LIST簽名為: " + TD_LIST); System.out.println("對象的TD_STRING簽名為: " + TD_STRING); // 線程一 Thread threadOne = new Thread(() -> { TD_STRING.set(new String("JustryDeng")); TD_LIST.set(Arrays.asList(1, 3, 5, 7, 9)); // sleep幾秒,保證getThreadLocalMapTableInfo(xxx)執行時,線程尚未結束 try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } }); threadOne.start(); // 線程二 Thread threadTwo = new Thread(() -> { TD_STRING.set(new String("鄧沙利文")); TD_LIST.set(Arrays.asList(2, 4, 6, 8, 10)); // sleep幾秒,保證getThreadLocalMapTableInfo(xxx)執行時,線程尚未結束 try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } }); threadTwo.start(); System.out.println(getThreadLocalMapTableInfo(threadOne)); System.out.println(getThreadLocalMapTableInfo(threadTwo)); }
- 控制台輸出: 上圖中隻能證明key是ThreadLocal的引用,但不能證明是弱引用,這個隻需要看一下源碼(如下圖)就可知其實際上是弱引用了:
ThreadLocal資料觀察及原理驗證ThreadLocal原理(簡述):ThreadLocal資料觀察及驗證: ThreadLocal資料觀察及原理驗證ThreadLocal原理(簡述):ThreadLocal資料觀察及驗證:
- 控制台輸出:
由此可見,本文開頭的原理介紹得到了驗證!
^_^ 如有不當之處,歡迎指正
^_^ 參考連結
https://www.cnblogs.com/micrari/p/6790229.html
^_^ 本文已經被收錄進《程式員成長筆記》 ,筆者JustryDeng