欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

ThreadLocal?在上下文傳值場景實踐源碼

 更新時間:2022年03月11日 08:34:14   作者:Q.E.D.  
這篇文章主要為大家介紹了ThreadLocal在上下文傳值場景下的實踐源碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步

開篇語

我們在 《打動面試官:線程池流程編排中的運用實戰(zhàn)》一文中將流程引擎簡單地完善了一下,本文在其基礎上繼續(xù)進行改造,建議同學可以先看看 GitHub 上的代碼,或者看看之前的文章。

1、回顧

流程引擎編排的對象,我們稱為組件(就是 SpringBean),之前我們給組件定義了通用的接口,組件實現時就實現這個接口,代碼如下:

圖片描述

我們定義了 DomainAbilityBean 接口,入參和出參都是 FlowContent,FlowContent 我們稱為上下文。

2、ThreadLocal 實現 

 上下文傳參除了 FlowContent 實現外,ThreadLocal 也是可以實現的,我們來演示一下:

2.1、定義 ThreadLocal 上下文工具類

首先我們使用 ThreadLocal 定義了上下文工具類,并且定義了 put、get 方法,方便使用,代碼如下:

public class ContextCache implements Serializable {
  private static final long serialVersionUID = 2136539028591849277L;
  // 使用 ThreadLocal 緩存上下文信息
  public static final ThreadLocal<Map<String,String>> CACHE = new ThreadLocal<>();
  /**
   * 放數據
   * @param sourceKey
   */
  public static final void putAttribute(String sourceKey,String value){
    Map<String,String> cacheMap = CACHE.get();
    if(null == cacheMap){
      cacheMap = new HashMap<>();
    }
    cacheMap.put(sourceKey,value);
    CACHE.set(cacheMap);
  }
  /**
   * 拿數據
   * @param sourceKey
   */
  public static final String getAttribute(String sourceKey){
    Map<String,String> cacheMap = CACHE.get();
    if(null == cacheMap){
      return null;
    }
    return cacheMap.get(sourceKey);
  }
}

如果你想往 ThreadLocal 放數據,調用 ContextCache.putAttribute 方法,如果想從 ThreadLocal 拿數據,調用 ContextCache.getAttribute 方法即可。

我們寫了兩個組件,一個組件放數據,一個組件拿數據,如下:

圖片描述

 我們把兩個 SpringBean 注冊到流程注冊中心中,讓其按照先執(zhí)行 BeanThree 再執(zhí)行 BeanFive 的順序進行執(zhí)行,運行 DemoApplication 類的 main 方法進行執(zhí)行,執(zhí)行結果如下:

圖片描述

從打印的日志可以看到,在 Spring 容器管理的 SpringBean 中,ThreadLocal 也是可以儲存中間緩存值的。

3、開啟子線程 

 我們做一個實驗,我們在 BeanFive 中開啟子線程,然后再從 ThreadLocal 中拿值,看看能否拿到值,BeanFive 的代碼修改成如下:

圖片描述

 我們再來運行一下,打印的日志如下:

圖片描述

 從打印的日志中,我們發(fā)現在子線程中從 ThreadLocal 取值時,并沒有取得值,這個原因主要是我們之前說的,線程在創(chuàng)建的時候,并不會把父線程的 ThreadLocal 中的值拷貝給子線程的 ThreadLocal,解決方案就是把 ThreadLocal 修改成 InheritableThreadLocal,代碼修改如下:

圖片描述

 我們再次運行,結果如下:

圖片描述

從運行結果看,我們成功的在子線程中拿到值。 

4、線程池 + ThreadLocal

 如果是拿數據的 springBean 是丟給線程池執(zhí)行的,我們能夠成功的從 ThreadLocal 中拿到數據么?

首先我們在放數據的 springBean 中,把放的值修改成隨機的,接著拿數據的 SpringBean 修改成異步執(zhí)行,代碼修改如下:

圖片描述

 為了能快速看到效果,我們把線程池的 coreSize 和 maxSize 全部修改成 3,并讓任務沉睡一段時間,這樣三個線程肯定消費不完任務,大量任務都會到隊列中去排隊,我們修改一下測試腳本,如下:

圖片描述

 我們期望的結果:

線程池中執(zhí)行的 BeanFive 可以成功從 ThreadLocal 中拿到數據;能夠從 ThreadLocal 拿到正確的數據,比如 BeanThree 剛放進 key1,value5,那么期望在 BeanFive 中根據 key1 能拿出 value5,而不是其它值。

我們運行一下,結果如下:

圖片描述

 從結果中可以看到,并沒有符合我們的預期,我們往 ThreadLocal 中 put 進很多值,但最后拿出來的值卻很多都是 value379,都為最后 put 到 ThreadLocal 中的值。

這個原因主要是 ThreadLocal 存儲的 HashMap 的引用都是同一個,main 主線程可以修改 HashMap 中的值,子線程從 ThreadLocal 中拿值時,也是從 HashMap 中拿值,從而導致不能把 put 的值通過 ThreadLocal 正確的傳遞給子線程。

為了證明是這個原因,我們在從 ThreadLocal 放、拿值的地方,把 HashMap 的內存地址都打印出來,改動代碼如下:

 圖片描述

 我們再次運行測試代碼,運行的結果如下:

圖片描述

 從測試結果中可以看到,不管是主線程還是子線程和 ThreadLocal 進行交互時,HashMap 都是同一個,也就是說 ThreadLocal 中保存的 HashMap 是共享的,這就導致了線程安全的問題,子線程讀取到的值就會混亂掉。

5、解決方案

 針對這個問題,我們提出了一種解決方案,在把任務提交到線程池時,我們進行 HashMap 的拷貝,這樣子線程的 HashMap 和 main 線程的 HashMap 就不同了,可以解決上面的問題。

我們提交任務時, 使用的是 Runnable,要實現 HashMap 的拷貝的話,我們需要把 Runnable 進行一層包裝,包裝的代碼如下:

圖片描述

運行結果如下:

圖片描述

 從運行結果中可以看出,線程池拿出來的 value 已經是正確的了。

6、總結

 本文通過 ThreadLocal 來改造流程引擎中的上下文傳遞,希望能夠加深大家對 ThreadLocal 的認識和使用技巧,有興趣的同學可以把我們的代碼下載下來,跑跑看。

以上就是ThreadLocal 在上下文傳值場景下的實踐的詳細內容,更多關于ThreadLocal 上下文傳值場景實踐的資料請關注腳本之家其它相關文章!

相關文章

  • 詳解Java中NullPointerException的處理方法

    詳解Java中NullPointerException的處理方法

    這篇文章將帶大家來單獨看一個很常見的異常--空指針異常,這個可以說是每個Java程序員都必知的異常,所以我們不得不單獨學習一下,文中有詳細的代碼示例,需要的朋友可以參考下
    2023-08-08
  • 項目總結之HttpURLConnection的disconnect的問題

    項目總結之HttpURLConnection的disconnect的問題

    這篇文章主要介紹了項目總結之HttpURLConnection的disconnect的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • 一行命令同時修改maven項目中多個module的版本號的方法

    一行命令同時修改maven項目中多個module的版本號的方法

    這篇文章主要介紹了一行命令同時修改maven項目中多個module的版本號的方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-06-06
  • Spring boot通過切面,實現超靈活的注解式數據校驗過程

    Spring boot通過切面,實現超靈活的注解式數據校驗過程

    這篇文章主要介紹了Spring boot通過切面,實現超靈活的注解式數據校驗過程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java實現多線程文件下載的代碼示例

    Java實現多線程文件下載的代碼示例

    本篇文章主要介紹了Java實現多線程下載的代碼示例,Java多線程可以充分利用CPU的資源,具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-02-02
  • 基于@JsonFormat的導包問題

    基于@JsonFormat的導包問題

    這篇文章主要介紹了關于@JsonFormat的導包問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • java集合求和最大值最小值示例分享

    java集合求和最大值最小值示例分享

    這篇文章主要介紹了java集合求和最大值最小值示例
    2014-01-01
  • Java中序列化與反序列化的特性解讀

    Java中序列化與反序列化的特性解讀

    這篇文章主要介紹了Java中序列化與反序列化的特性解讀,當我們需要將內存中的對象持久化到磁盤,數據庫中時, 當我們需要與瀏覽器進行交互時,當我們需要實現 RPC 時, 這個時候就需要序列化和反序列化了,需要的朋友可以參考下
    2023-08-08
  • Kotlin-Coroutines中的async與await異步協(xié)程管理

    Kotlin-Coroutines中的async與await異步協(xié)程管理

    這篇文章主要為大家介紹了Kotlin-Coroutines中的async與await異步協(xié)程管理,提升程序性能解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10
  • Spring?boot?easyexcel?實現復合數據導出、按模塊導出功能

    Spring?boot?easyexcel?實現復合數據導出、按模塊導出功能

    這篇文章主要介紹了Spring?boot?easyexcel?實現復合數據導出、按模塊導出,實現思路流程是準備一個導出基礎填充模板,默認填充key,本文給大家介紹的非常詳細,需要的朋友可以參考下
    2023-09-09

最新評論