ThreadLocal數據觀察及原理驗證

筆者日常 需要學點前端的東西!從今天開始,以後發博客按照二後一前的頻率進行發表!!!


聲明 本文主要演示如何觀察及證明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的引用,但不能證明是弱引用,這個只需要看一下源碼(如下圖)就可知其實際上是弱引用了:
      在這裏插入圖片描述

由此可見,本文開頭的原理介紹得到了驗證!


^_^ 如有不當之處,歡迎指正

^_^ 參考鏈接
        https://www.cnblogs.com/micrari/p/6790229.html

^_^ 本文已經被收錄進《程序員成長筆記(六)》,筆者JustryDeng

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章