用ThreadLocal來優化下代碼吧

最近接手了一個老項目,看到一個頗有意思的現象。git

這個項目中大量的方法入參都會帶上user信息,好比這樣github

用ThreadLocal來優化下代碼吧

 

它的意圖是但願在方法內使用user的信息,可是如此大範圍的傳遞用戶信息,第一感受就是不優雅。那有什麼辦法能夠優化一下呢?面試

用ThreadLocal來優化下代碼吧

 

咱們第一反應是,能夠存一個全局變量,在初始位置將用戶信息存入全局變量,而後在須要的地方去get一下。微信

那在WEB應用中,每一個請求都是一個獨立線程,怎麼去標記呢?學習

能夠用線程的id去做爲map的key,將該請求的用戶信息做爲map的value。優化

咦?很熟悉的感受。線程

用ThreadLocal來優化下代碼吧

 

沒錯,Java已經幫咱們封裝好了這麼一個對象,它就是咱們今天要說的ThreadLocal。翻譯

  • 什麼是ThreadLocal
  • 如何使用ThreadLocal優化userid層層傳遞的問題
  • ThreadLocal原理是啥
  • ThreadLocal的其餘使用場景

1.什麼是ThreadLocal

先來看下JDK的註釋:設計

用ThreadLocal來優化下代碼吧

 

簡單翻譯過來,就是說:3d

ThreadLocal提供了線程隔離的局部變量,經過get( )和set( )方法操做當前線程對應的變量,並且不會和其餘線程衝突,實現了基於線程的數據隔離。

2.如何使用ThreadLocal進行優化

話很少說,基於咱們開頭的例子,我火燒眉毛地用ThreadLocal來優化一下。

2.1 構建基於ThreadLocal的上下文

定義一個SessionUser類,存儲用戶信息,包括用戶id、用戶名。

而後定義一個基於ThreadLocal的上下文SessionUserContext,代碼以下所示。

用ThreadLocal來優化下代碼吧

 


2.2 信息存入ThreadLocal中

在咱們的優化案例中,就是存入用戶信息。

解析請求中的用戶信息有不少方法。本文以HandlerIntercept爲例,說明下MVC中的一種方式。

  • 實現HandlerIntercept接口
  • 重寫preHandler方法
  • 解析HttpServletRequest,獲取用戶信息
  • 用戶信息存於SessionUserContext

源碼以下所示。

用ThreadLocal來優化下代碼吧

 

2.3 在須要的地方獲取信息

本來須要傳入CurrentUser的參數均可以去掉了。

在須要用戶信息的時候,直接從SessionUserContext中獲取便可。

用ThreadLocal來優化下代碼吧

 

哈哈,是否是看起來一會兒清爽了不少。

能夠在任何地方獲取user信息,再也不須要層層傳遞用戶信息了。

用ThreadLocal來優化下代碼吧

 

3.ThreadLocal實現原理

上面咱們已經知道了怎麼經過ThreadLocal進行優化。

下面,咱們要 知其然知其因此然,一塊兒看看ThreadLocal實現原理吧。

3.1 set方法

Set方法應該是ThreadLocal的核心邏輯了。

主要三步:

  • 獲取當前線程
  • 獲取ThreadLocalMap對象
  • 若是ThreadLocalMap對象存在,則將當前線程對象做爲key,要存儲的對象做爲value存到map中 若是ThreadLocalMap對象不存在,就調用creatMap( )進行建立
用ThreadLocal來優化下代碼吧

 

3.2 ThreadLocalMap是什麼。

ThreadLocalMap是一個定義在ThreadLocal類內部的靜態類,裏面還定義了一個Entry類做爲存儲值的地方。

ThreadLocalMap的key是當前ThreadLocal對象,value是咱們要存儲的值(對象)。

用ThreadLocal來優化下代碼吧

 

調用creatMap的時候,就是新建一個ThreadLocalMap對象

用ThreadLocal來優化下代碼吧

 

同時,ThreadLocalMap在Thread類中做爲一個屬性存在。

用ThreadLocal來優化下代碼吧

 

每一個線程Thread維護了ThreadLocalMap這麼一個Map,這個map的key是LocalThread對象自己,value則是要存儲的對象

3.3 get方法

Get方法就比較簡單了,就是從map中取值的過程。

用ThreadLocal來優化下代碼吧

 

3.4 ThreadLocal小結

如今,讓咱們從新梳理一遍,看看ThreadLocal是如何實現變量的線程隔離的:

  • 每一個Thread維護着一個ThreadLocalMap的引用
  • ThreadLocalMap是ThreadLocal的內部類,用Entry來進行存儲,key是ThreadLocal對象,值是傳遞進來的對象
  • 調用ThreadLocal的get()/set()方法時,實際上就是以ThreadLocal對象爲key,在ThreadLocalMap中讀寫value

 

4.實戰要點

在一開始的優化設計中,不知道你們有沒有注意到對ThreadLocal的remove調用。

用ThreadLocal來優化下代碼吧

 

這裏就須要談談ThreadLocal使用時的,兩個要點。尤爲是在使用線程池的時候使用ThreadLocal。

4.1 避免內存泄露

用ThreadLocal來優化下代碼吧

 

在ThreadLocalMap介紹的時候,咱們能夠看到,ThreadLocalMap是Thread的一個屬性。所以,ThreadLocalMap和Thread的生命週期是同樣的。

若是沒有手動刪除對應的ThreadLocal的key,那麼就會形成內存泄漏沒法回收。尤爲在線程池環境下,線程會被不斷複用。

4.2 線程池避免重複線程變量影響

之前文優化案例爲例。

在MVC中,每次請求進來會使用線程池複用線程。若是請求帶了用戶信息,那麼就會重置ThreadLocal對應的用戶信息,若是請求沒有帶用戶信息,必須手動清除一下當前ThreadLocal對應的變量,不然後面使用過程當中可能會形成混亂。

 

都看到最後了,原創不易,點個關注,點個贊吧~
文章持續更新,能夠微信搜索「阿丸筆記 」第一時間閱讀,回覆關鍵字【學習】有我準備的一線大廠面試資料。
知識碎片從新梳理,構建Java知識圖譜: github.com/saigu/JavaK…(歷史文章查閱很是方便)
相關文章
相關標籤/搜索