亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java多線程之ThreadLocal原理總結(jié)

 更新時(shí)間:2023年04月21日 11:10:45   作者:奔赴在自己的熱愛中  
這篇文章主要介紹了Java多線程ThreadLocal原理,同一個(gè)ThreadLocal所包含的對象,在不同的Thread中有不同的副本,文章中有詳細(xì)的代碼示例,需要的朋友參考一下

1、什么是 ThreadLocal:

ThreadLocal,即線程本地變量,如果你創(chuàng)建了一個(gè)變量,那么訪問這個(gè)變量的每個(gè)線程都會有這個(gè)變量的本地拷貝,多個(gè)線程操作這個(gè)變量的時(shí)候,實(shí)際操作自己本地內(nèi)存里面的變量,從而起到線程隔離的作用,避免了線程安全問題

ThreadLocal 適用于無狀態(tài),副本變量獨(dú)立后不影響業(yè)務(wù)邏輯的高并發(fā)場景,如果業(yè)務(wù)邏輯強(qiáng)依賴于變量副本,則不適合用 ThreadLocal 解決,需要另尋解決方案

應(yīng)用場景:

數(shù)據(jù)庫連接池會話管理中使用

2、ThreadLocal 的數(shù)據(jù)結(jié)構(gòu):

在 JDK8 中,每個(gè)線程 Thread 內(nèi)部都維護(hù)了一個(gè) ThreadLocalMap 的數(shù)據(jù)結(jié)構(gòu),ThreadLocalMap 中有一個(gè)由內(nèi)部類 Entry 組成的 table 數(shù)組,Entry 的 key 就是線程的本地化對象 ThreadLocal,而 value 則存放了當(dāng)前線程所操作的變量副本。每個(gè) ThreadLocal 只能保存一個(gè)副本 value,并且各個(gè)線程的數(shù)據(jù)互不干擾,如果想要一個(gè)線程保存多個(gè)副本變量,就需要創(chuàng)建多個(gè)ThreadLocal。

一個(gè) ThreadLocal 的值,會根據(jù)線程的不同,分散在 N 個(gè)線程中,所以獲取 ThreadLocal 的 value,有兩個(gè)步驟:

第一步,根據(jù)線程獲取 ThreadLocalMap

第二步,根據(jù)自身從 ThreadLocalMap 中獲取值,所以它的 this 就是 Map 的 Key

當(dāng)執(zhí)行 set() 方法時(shí),其值是保存在當(dāng)前線程的 ThreadLocal 變量副本中
當(dāng)執(zhí)行g(shù)et() 方法中,是從當(dāng)前線程的 ThreadLocal 的變量副本獲取。

所以對于不同的線程,每次獲取副本值時(shí),別的線程并不能獲取到當(dāng)前線程的副本值,形成了線程的隔離,互不干擾。

3、ThreadLocal 的核心方法:

ThreadLocal 對外暴露的方法有4個(gè):

1.initialValue()方法:返回為當(dāng)前線程初始副本變量值。
2.get()方法:獲取當(dāng)前線程的副本變量值。
3.set()方法:保存當(dāng)前線程的副本變量值。
4.remove()方法:移除當(dāng)前前程的副本變量值

 1、set()方法:

// 設(shè)置當(dāng)前線程對應(yīng)的ThreadLocal值
public void set(T value) {
    Thread t = Thread.currentThread(); // 獲取當(dāng)前線程對象
    ThreadLocalMap map = getMap(t);
    if (map != null) // 判斷map是否存在
        map.set(this, value); 
        // 調(diào)用map.set 將當(dāng)前value賦值給當(dāng)前threadLocal。
    else
        createMap(t, value);
        // 如果當(dāng)前對象沒有ThreadLocalMap 對象。
        // 創(chuàng)建一個(gè)對象 賦值給當(dāng)前線程
}
 
// 獲取當(dāng)前線程對象維護(hù)的ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
// 給傳入的線程 配置一個(gè)threadlocals
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

執(zhí)行流程:

1.獲得當(dāng)前線程,根據(jù)當(dāng)前線程獲得 map。
2.如果 map 不為空,則將參數(shù)設(shè)置到 map 中,當(dāng)前的 Threadlocal 作為 key。
3.如果 map 為空,則給該線程創(chuàng)建 map,設(shè)置初始值。

 2、get()方法:

public T get() {
    Thread t = Thread.currentThread();//獲得當(dāng)前線程對象
    ThreadLocalMap map = getMap(t);//線程對象對應(yīng)的map
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);// 以當(dāng)前threadlocal為key,嘗試獲得實(shí)體
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    // 如果當(dāng)前線程對應(yīng)map不存在
    // 如果map存在但是當(dāng)前threadlocal沒有關(guān)連的entry。
    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;
}

執(zhí)行流程:

(1)先嘗試獲得當(dāng)前線程,再根據(jù)當(dāng)前線程獲取對應(yīng)的 map
(2)如果獲得的 map 不為空,以當(dāng)前 threadlocal 為 key 嘗試獲得 entry
(3)如果 entry 不為空,返回值。
(4)如果 2 跟 3 出現(xiàn)無法獲得,則通過 initialValue 函數(shù)獲得初始值,然后給當(dāng)前線程創(chuàng)建新 map

 3、remove()方法:

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}

執(zhí)行流程:

首先嘗試獲取當(dāng)前線程,然后根據(jù)當(dāng)前線程獲得map,從map中嘗試刪除enrty。

4、initialValue() 方法:

protected T initialValue() {
    return null;
}

執(zhí)行流程:

(1)如果沒有調(diào)用 set() 直接 get(),則會調(diào)用此方法,該方法只會被調(diào)用一次,
(2)默認(rèn)返回一個(gè)缺省值null,如果不想返回null,可以O(shè)verride 進(jìn)行覆蓋。

4、ThreadLocal 的哈希沖突的解決方法:線性探測

和 HashMap 不同,ThreadLocalMap 結(jié)構(gòu)中沒有 next 引用
ThreadLocalMap 中解決哈希沖突的方式并非鏈表的方式,而是采用線性探測的方式,當(dāng)發(fā)生哈希沖突時(shí)就將步長加1或減1,尋找下一個(gè)相鄰的位置

流程說明:

1.根據(jù) ThreadLocal 對象的 hash 值,定位到 table 中的位置 i;

2.如果當(dāng)前位置是 null,就初始化一個(gè) Entry 對象放在位置 i 上;

3.如果位置 i 已經(jīng)有 Entry 對象了,如果這個(gè) Entry 對象的 key 與即將設(shè)置的 key 相同,那么重新設(shè)置 Entry 的 value;

4.如果位置 i 的 Entry 對象和 即將設(shè)置的 key 不同,那么尋找下一個(gè)空位置;

5、ThreadLocal 的內(nèi)存泄露:

在使用 ThreadLocal 時(shí),當(dāng)使用完變量后,必須手動調(diào)用 remove() 方法刪除 entry 對象,否則會造成 value 的內(nèi)存泄露,嚴(yán)格來說,ThreadLocal 是沒有內(nèi)存泄漏問題,有的話,那也是忘記執(zhí)行 remove() 引起的

內(nèi)存泄露的根本原因在于 ThreadLocalMap 的生命周期與當(dāng)前線程 CurrentThread 的生命周期相同,且 ThreadLocal 使用完沒有進(jìn)行手動刪除導(dǎo)致的

ThreadLocal 的內(nèi)存泄露與強(qiáng)弱引用無關(guān),那么為什么還要用弱引用呢?

(1)Entry 中的 key(Threadlocal)是弱引用,目的是將 ThreadLocal 對象的生命周期跟線程周期解綁,用 WeakReference 弱引用關(guān)聯(lián)的對象,只能生存到下一次垃圾回收之前,GC發(fā)生時(shí),不管內(nèi)存夠不夠,都會被回收。

(2)當(dāng)我們使用完 ThreadLocal,而 Thread 仍然運(yùn)行時(shí),即使忘記調(diào)用 remove() 方法, 弱引用也會比強(qiáng)引用多一層保障:當(dāng) GC 發(fā)生時(shí),弱引用的 ThreadLocal 被收回,那么 key 就為 null 了。而 ThreadLocalMap 中的 set()、get() 方法,會針對 key == null (也就是 ThreadLocal 為 null) 的情況進(jìn)行處理,如果 key == null,則系統(tǒng)認(rèn)為 value 也應(yīng)該是無效了應(yīng)該設(shè)置為 null,也就是說對應(yīng)的 value 會在下次調(diào)用 ThreadLocal 的 set()、get() 方法時(shí),執(zhí)行底層 ThreadLocalMap 中的 expungeStaleEntry() 方法進(jìn)行清除無用的 value,從而避免內(nèi)存泄露。

6、ThreadLocal 的應(yīng)用場景:

(1)Hibernate 的 session 獲?。好總€(gè)線程訪問數(shù)據(jù)庫都應(yīng)當(dāng)是一個(gè)獨(dú)立的 session 會話,如果多個(gè)線程共享同一個(gè) session 會話,有可能其他線程關(guān)閉連接了,當(dāng)前線程再執(zhí)行提交時(shí)就會出現(xiàn)會話已關(guān)閉的異常,導(dǎo)致系統(tǒng)異常。

使用 ThreadLocal 的方式能避免線程爭搶session,提高并發(fā)安全性。

(2)Spring 的事務(wù)管理:事務(wù)需要保證一組操作同時(shí)成功或失敗,意味著一個(gè)事務(wù)的所有操作需要在同一個(gè)數(shù)據(jù)庫連接上,Spring 采用 Threadlocal 的方式,來保證單個(gè)線程中的數(shù)據(jù)庫操作使用的是同一個(gè)數(shù)據(jù)庫連接,同時(shí)采用這種方式可以使業(yè)務(wù)層使用事務(wù)時(shí)不需要感知并管理 connection 對象,通過傳播級別,巧妙地管理多個(gè)事務(wù)配置之間的切換,掛起和恢復(fù)

7、如果想共享線程的 ThreadLocal 數(shù)據(jù)怎么辦 ?

使用 InheritableThreadLocal 可以實(shí)現(xiàn)多個(gè)線程訪問 ThreadLocal 的值

我們在主線程中創(chuàng)建一個(gè) InheritableThreadLocal 的實(shí)例,然后在子線程中得到這個(gè)InheritableThreadLocal實(shí)例設(shè)置的值。

private void test() {    
final ThreadLocal threadLocal = new InheritableThreadLocal();       
threadLocal.set("主線程的ThreadLocal的值");    
Thread t = new Thread() {        
    @Override        
    public void run() {            
      super.run();            
      Log.i( "我是子線程,我要獲取其他線程的ThreadLocal的值 ==> " + threadLocal.get());        
    }    
  };          
  t.start(); 
} 

8、為什么一般用 ThreadLocal 都要用 static?

ThreadLocal 能實(shí)現(xiàn)線程的數(shù)據(jù)隔離,不在于它自己本身,而在于 Thread 的 ThreadLocalMap,所以,ThreadLocal 可以只實(shí)例化一次,只分配一塊存儲空間就可以了,沒有必要作為成員變量多次被初始化。

到此這篇關(guān)于Java多線程之ThreadLocal原理總結(jié)的文章就介紹到這了,更多相關(guān)Java多線程ThreadLocal內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • spring-mvc/springboot使用MockMvc對controller進(jìn)行測試

    spring-mvc/springboot使用MockMvc對controller進(jìn)行測試

    這篇文章主要介紹了spring-mvc/springboot使用MockMvc對controller進(jìn)行測試,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-11-11
  • 五種SpringBoot實(shí)現(xiàn)數(shù)據(jù)加密存儲的方式總結(jié)

    五種SpringBoot實(shí)現(xiàn)數(shù)據(jù)加密存儲的方式總結(jié)

    這篇文章主要為大家詳細(xì)介紹了五種常見數(shù)據(jù)加密存儲的方法(結(jié)合SpringBoot和MyBatisPlus框架進(jìn)行實(shí)現(xiàn)),文中的示例代碼講解詳細(xì),需要的可以參考下
    2023-11-11
  • Java判斷List中有無重復(fù)元素的方法

    Java判斷List中有無重復(fù)元素的方法

    今天小編就為大家分享一篇Java判斷List中有無重復(fù)元素的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-07-07
  • 解決springcloud-gateway限流遇到的問題

    解決springcloud-gateway限流遇到的問題

    這篇文章主要介紹了解決springcloud-gateway限流遇到的問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 淺談hibernate中多表映射關(guān)系配置

    淺談hibernate中多表映射關(guān)系配置

    下面小編就為大家?guī)硪黄獪\談hibernate中多表映射關(guān)系配置。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-06-06
  • Java?超詳細(xì)講解字符流

    Java?超詳細(xì)講解字符流

    字符流就是在字節(jié)流的基礎(chǔ)上,加上編碼,形成的數(shù)據(jù)流,字符流出現(xiàn)的意義是因?yàn)樽止?jié)流在操作字符時(shí),可能會有中文導(dǎo)致的亂碼,所以由字節(jié)流引申出了字符流
    2022-04-04
  • SpringSecurity如何實(shí)現(xiàn)配置單個(gè)HttpSecurity

    SpringSecurity如何實(shí)現(xiàn)配置單個(gè)HttpSecurity

    這篇文章主要介紹了SpringSecurity如何實(shí)現(xiàn)配置單個(gè)HttpSecurity,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Java 正確地從類路徑中獲取資源

    Java 正確地從類路徑中獲取資源

    Java 有能力從類路徑中查找獲取資源,可將資源放在 CLASSPATH 里,也可打包到 Jar 中。本文將具體講述獲取資源的步驟,感興趣的朋友可以了解下
    2021-05-05
  • Java concurrency線程池之線程池原理(四)_動力節(jié)點(diǎn)Java學(xué)院整理

    Java concurrency線程池之線程池原理(四)_動力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要為大家詳細(xì)介紹了Java concurrency線程池之線程池原理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • Java多線程之簡單模擬售票功能

    Java多線程之簡單模擬售票功能

    這篇文章主要介紹了Java多線程之簡單模擬售票功能,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java的小伙伴們有很好地幫助,需要的朋友可以參考下
    2021-04-04

最新評論