ThreadLocal介紹

ThreadLocal 能做什么

ThreadLocal 是 JDK 提供的一個工具類,它可以為每個使用它的線程創(chuàng)建一個線程本地的副本,從而能保證多個線程在訪問時的安全問題。當(dāng)多個線程在使用這個變量時,其實是在使用自己線程本地內(nèi)存的變量,由于是線程級別的,因此就能完全避免多個線程訪問時,資源競爭的安全問題。

ThreadLocal 的原理

要講解 ThreadLocal 的原理,我們首先需要看 JDK 的源碼,了解了 ThreadLocal 類的主要方法實現(xiàn)就能理解其原理了。使用 ThreadLocal 時主要就是調(diào)用它的兩個方法即 get 方法和 set 方法,下面直接貼出他們的源碼。

首先看 set 方法:

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

由于 ThreadLocal 是一個帶泛型的類,所以在創(chuàng)建 ThreadLocal 對象時,需要指定對應(yīng)的泛型而 set 方法的入?yún)⒕褪嵌x的泛型對象;
接下來看具體的邏輯:
1首先創(chuàng)建了一個當(dāng)前調(diào)用 set 方法的線程實例對象;
2、調(diào)用 getMap 方法,入?yún)楫?dāng)前線程的實例對象,返回一個當(dāng)前線程對象的 threadLocals 屬性值,threadLocals 是Thread 類的一個屬性,也就是說 Java 的所有線程對象都有這個屬性,這個屬性是一個 ThreadLocalMap 對象(ThreadLocalMap 其實就是一個簡化版的 HashMap);
3、如果返回的 ThreadLocalMap 為 null(threadLocals 屬性的默認值就是 null),則會創(chuàng)建一個 ThreadLocalMap 對象,會調(diào)用createMap 方法;
4、createMap 方法接收兩個入?yún)ⅲ粋€為 當(dāng)前調(diào)用線程的實例對象,一個為 firstValue(默認 firstValue 為 null)因此這個方法會創(chuàng)建一個 key 為當(dāng)前調(diào)用線程實例,value 為當(dāng)前 set 方法傳入的值 的ThreadLocalMap 對象,并將這個對象賦值給當(dāng)前線程實例的 threadLocals 屬性;
總結(jié):要想了解 set 方法,就需要了解 threadLocals 屬性,簡單理解它就是一個 key 為當(dāng)前線程實例對象的 HashMap;set 方法就是把 set 的入?yún)⒃O(shè)置到這個 hashMap 的 value 中的過程;

接下來看 get 方法:

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

其實 get 方法和 set 方法一樣,本質(zhì)上都是操作當(dāng)前線程實例的 threadLocals 屬性,只是 get 方法是獲取該屬性的 value 值;
接下來看具體的邏輯:
1、前面的兩個操作和 set 方法一樣,獲取當(dāng)前調(diào)用線程實例的 threadLocals 屬性值;
2、如果不為空則直接返回當(dāng)前屬性值的 value 值;
3、如果為空則為當(dāng)前線程實例初始化一個 value 值為空的 threadLocals 屬性(initialValue 方法的返回值為 null);

原理總結(jié)

1、通過以上源碼介紹,其實 ThreadLocal 是一個帶泛型的類,使用的時候就是調(diào)用它的 get 方法和它的 set 方法;
2、這兩個方法的本質(zhì)就是操作當(dāng)前調(diào)用線程實例的 threadLocals 屬性值;
3、threadLocals 屬性是一個 Thread LocalMap 對象,這個對象就是一個簡單的 HashMap,而它的 key 為當(dāng)前線程的實例對象,value 為某個需要被共享的變量值;
4、由于這個值是放在當(dāng)前線程實例的一個 Map 中,因此多個線程訪問的時候是訪問自己本地的變量,因此不可能有多線程安全訪問的問題;

ThreadLocal 應(yīng)用 Demo

先上代碼

public static void main(String[] args) {
        ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();
        stringThreadLocal.set("main threadLocal");
        System.out.println("main threadLocal value:"+stringThreadLocal.get());
        Thread threadA = new Thread(() -> {
            stringThreadLocal.set("threadA threadLocal");
            System.out.println("threadA threadLocal value:" + stringThreadLocal.get());
        });
        Thread threadB = new Thread(() -> {
            stringThreadLocal.set("threadB threadLocal");
            System.out.println("threadB threadLocal value:" + stringThreadLocal.get());
        });
        threadA.start();
        threadB.start();
    }

結(jié)果

main threadLocal value:main threadLocal
threadA threadLocal value:threadA threadLocal
threadB threadLocal value:threadB threadLocal

上面是對 ThreadLocal 最簡單的使用,雖然對 ThreadLocal 來說是一個對象,并且是被三個線程共同訪問,正常情況下由于多線程的問題一定會表現(xiàn)為結(jié)果不可預(yù)知;但是由于 ThreadLocal 的特性,是當(dāng)前線程的本地副本設(shè)置屬性值,因此不會出現(xiàn)多線程安全的問題;可以用它來記錄一下上下文的一內(nèi)容信息,或者用來設(shè)置一個日志的唯一標(biāo)識 uuid 等,來標(biāo)識當(dāng)前線程的執(zhí)行日志,方便查找問題。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容