天天看點

ThreadLocal原了解析(一)之---基礎屬性

最近在學習ThreadLocal相關知識,進行了下記錄。

一:ThreadLocal是什麼?

ThreadLocal是服務于Thread的一種本地私有資料機制,threadlocalvariable(線程局部變量), 即為每一個使用該變量的線程都提供一個變量值的副本,與使用的線程綁定,每一個線程都可以獨立地改變自己的副本,不會産生沖突。 其在Thread處于活躍狀态,并且threadLocal對象可用的情況下,就可以在其中存放資料。線上程被銷毀之後,則ThreadLocal中使用的資源和記憶體同時也被回收。本質上,JVM通過ThreadLocal為多線程情況下,進行資源的通路提供了一種隔離機制,簡化了程式設計的複雜度。

二:ThreadLocal的實作原理是什麼?

每一個線程都有一個對應的Thread對象,而Thread類有一個成員變量,它是一個Map集合,這個Map集合的key就是ThreadLocal的引用,而value就是目前線程在key所對應的ThreadLocal中存儲的值。當某個線程需要擷取存儲在ThreadLocal變量中的值時,ThreadLocal底層會擷取目前線程的Thread對象中的Map集合,然後以ThreadLocal作為key,從Map集合中查找value值。這就是ThreadLocal實作線程獨立的原理。也就是說,ThreadLocal能夠做到線程獨立,是因為值并不存在ThreadLocal中,而是存儲線上程對象中。下面我們根據ThreadLocal中兩個最重要的方法來确認這一點。

ThreadLocal原了解析(一)之---基礎屬性

三:接下來就先了解下ThreadLocal的相關屬性

3.1 threadLocalHashCode:該字段的含義是:

線程擷取threadLocal.get()時, 如果第一次在某個 threadLocal對象上get時,會給目前線程配置設定一個value, 如果這個value和目前的threadLocal對象 被包裝成一個entry 其中 key 是 threadLocal對象,value是threadLocal對象給目前線程生成的value.

這個entry 存放到目前線程 threadLocals 這個map 的哪個桶位?

于目前threadLocal對象的threadLocalHashCode有關系

使用threadLocalHashCode & (table.length-1)得到的位置,就是目前entry需要存放的位置

3.2 nextHashCode:建立threadLocal對象時,每建立一個threadLocal對象 就會使用nextHashCode 配置設定一個hash值給這個對象

private static AtomicInteger nextHashCode =
            new AtomicInteger();
           

3.3 HASH_INCREMENT: 每建立一個threadLocal對象,這個ThreadLocal.nextHashCode 這個值就會增長 0x61c88647。這個值就是 斐波那契數。

3.4 nextHashCode()方法: 建立新的ThreadLocal對象時,會給目前對象配置設定一個hash.使用的就是這個方法

private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }
           

3.5 initialValue()方法:預設傳回null,一般需要重寫

protected T initialValue() {
        return null;
    }
           

3.6 接下來就是比較重要的三個方法之一get()方法:get方法的作用是:

傳回目前線程于目前ThreadLocal對象相關聯的 線程局部變量,這個變量隻有目前線程能通路到。

如果目前線程沒有配置設定,則給目前線程去配置設定(使用initialValue()方法)

在get()方法中, 先擷取目前線程,在通過getMap()方法擷取threadLocals map引用 。

如果條件成立,說明目前線程已經擁有了自己的ThreadLocalMap對象, 同時entry如有有值,表示目前線程已經初始化過了,于目前線程threadLocal對象關聯的 線程局部變量,并傳回線程的value值

如果map活着entry為null,則會進行初始化.

// 傳回目前線程于目前ThreadLocal對象相關聯的 線程局部變量,這個變量隻有目前線程能通路到。
    // 如果目前線程沒有配置設定,則給目前線程去配置設定(使用initialValue()方法)
    public T get() {
        // 擷取目前線程
        Thread t = Thread.currentThread();
        // 擷取目前線程Thread對象的 threadLocals map 引用
        ThreadLocalMap map = getMap(t);
        // 如果條件成立,說明目前線程已經擁有自己的 ThreadLocalMap 對象了
        if (map != null) {
            // 可以:目前threadLocal對象
            // 調用map.getEntry()方法,擷取threadLocalMap中 該threadLocal 關聯的 entry
            ThreadLocalMap.Entry e = map.getEntry(this);
            // 條件成立,目前線程初始化過 與目前線程threadLocal對象相關聯的 線程局部變量
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                // 傳回value值
                return result;
            }
        }

        // 執行到這裡,map是null 或者 entry是 null

        // 初始化目前線程與目前threadLocal對象 相關聯的value
        // 且目前線程如果沒有threadLocalMap的話,還會初始化建立map
        return setInitialValue();
    }
           

在這個方法中,有兩個小方法;getMap()方法和setInitialValue()方法

3.7 getMap()方法: 主要作用是傳回目前線程的 threadLocals

ThreadLocalMap getMap(Thread t) {
        // 傳回目前線程的 threadLocals
        return t.threadLocals;
    }
           

3.8setInitialValue()方法:進行目前線程的初始化

先調用initalValue()方法進行初始化,擷取目前線程。進行調用getMap()方法

如果map有值,會儲存目前threadLocal與目前線程相關聯的value。這個map的key 就是目前線程的threadLocal對象.value就是 線程與目前threadLocal相關的局部變量

如果map為null ,會進行建立。傳入的k為目前線程和目前線程的value值

最後傳回線程與目前threadLocal的局部變量

private T setInitialValue() {
        // 調用目前ThreadLocal對象的initialValue方法
        // value 就是目前ThreadLocal對象與目前線程生成的線程的局部變量
        T value = initialValue();
        //擷取目前線程
        Thread t = Thread.currentThread();
        // 擷取目前線程内部的threadLocals threadLocalMap對象
        ThreadLocalMap map = getMap(t);
        // 條件成立,說明目前線程内部初始化過threadLocalMap對象了(線程的threadLocals 隻會初始化一次)
        if (map != null)
            // 儲存目前threadLocal與目前線程相關聯的 value
            // key:目前threadLocal對象
            // value:線程于目前threadLocal相關的局部變量
            map.set(this, value);
        else
            // 不成立的話,說明目前線程内部還未初始化
            // 參數一:目前線程; 參數二:目前線程的value值
            createMap(t, value);

        //傳回線程于目前threadLocal的局部變量
        return value;
    }
           

這這個方法中,有個新的方法createMap():方法

3.9.createMap()方法:進行建立一個新的threadLocalMap

void createMap(Thread t, T firstValue) {
        // 傳遞t 的意義就是 要通路 目前這個線程 t.threadLocals字段,給這個字段初始化

        // new ThreadLocalMap(this,firstValue) 建立一個ThreadLocalMap對象
        // 初始化 key: this目前threadLocal對象
        // value: 線程于目前threadLocal相關的局部變量
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
           

3.11接下來一個方法,也是ThreadLocal中最重要的三個方法之一 set()方法

主要作用是:修改目前線程與目前threadLocal對象相關的局部變量

通過先擷取目前線程,再擷取ThreadLocalMap ,判斷map是否存在。如果map不為null,表示目前線程已經初始化了,隻需要map.set即可,進行重寫或者添加。

如果map為null,說明還未建立,需要調用createMap()進行初始化的建立

// 修改目前線程于目前threadLocal對象相關聯的 線程局部變量
    public void set(T value) {
        // 擷取目前線程
        Thread t = Thread.currentThread();
        // 擷取目前線程threadLocalMap對象
        ThreadLocalMap map = getMap(t);
        // 條件成立, 說明目前線程的threadLocal已經初始化過了
        if (map != null)
            // 調用threadLocalMap.set方法,進行重寫或者添加
            map.set(this, value);
        else
            // 執行到這,說明的哪個區線程還未建立
            createMap(t, value);
    }
           

3.12第三個重要的方法就是remove()方法: 顧名思義就是移除

移除目前線程于目前threadLocal對象相關聯的 線程局部變量

擷取目前的線程的map,如果存在的話,就進行remove移除

// 移除目前線程于目前threadLocal對象相關聯的 線程局部變量
    public void remove() {
        // 擷取目前線程的map
        ThreadLocalMap m = getMap(Thread.currentThread());
        // 如果成立,直接remove
        if (m != null)
            m.remove(this);
    }