ThreadLocal Thread ThreadLocalMap 之間的關係

前言

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對象來觀察數據。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章