最近接手了一個老項目,看到一個頗有意思的現象。web
這個項目中大量的方法入參都會帶上user信息,好比這樣微信
它的意圖是但願在方法內使用user的信息,可是如此大範圍的傳遞用戶信息,第一感受就是不優雅。那有什麼辦法能夠優化一下呢?優化
咱們第一反應是,能夠存一個全局變量,在初始位置將用戶信息存入全局變量,而後在須要的地方去get一下。url
那在WEB應用中,每一個請求都是一個獨立線程,怎麼去標記呢?spa
能夠用線程的id去做爲map的key,將該請求的用戶信息做爲map的value。.net
咦?很熟悉的感受。線程
沒錯,Java已經幫咱們封裝好了這麼一個對象,它就是咱們今天要說的ThreadLocal。翻譯
什麼是ThreadLocal設計
如何使用ThreadLocal優化userid層層傳遞的問題3d
ThreadLocal原理是啥
ThreadLocal的實戰要點
1.什麼是ThreadLocal
先來看下JDK的註釋:
簡單翻譯過來,就是說:
ThreadLocal提供了線程隔離的局部變量,經過get( )和set( )方法操做當前線程對應的變量,並且不會和其餘線程衝突,實現了基於線程的數據隔離。
2.如何使用ThreadLocal進行優化
話很少說,基於咱們開頭的例子,我火燒眉毛地用ThreadLocal來優化一下。
2.1 構建基於ThreadLocal的上下文
定義一個SessionUser類,存儲用戶信息,包括用戶id、用戶名。
而後定義一個基於ThreadLocal的上下文SessionUserContext,代碼以下所示。
2.2 信息存入ThreadLocal中
在咱們的優化案例中,就是存入用戶信息。
解析請求中的用戶信息有不少方法。本文以HandlerIntercept爲例,說明下MVC中的一種方式。
實現HandlerIntercept接口
重寫preHandler方法
解析HttpServletRequest,獲取用戶信息
用戶信息存於SessionUserContext
源碼以下所示。
2.3 在須要的地方獲取信息
本來須要傳入CurrentUser的參數均可以去掉了。
在須要用戶信息的時候,直接從SessionUserContext中獲取便可。
哈哈,是否是看起來一會兒清爽了不少。
能夠在任何地方獲取user信息,再也不須要層層傳遞用戶信息了。
3.ThreadLocal實現原理
上面咱們已經知道了怎麼經過ThreadLocal進行優化。
下面,咱們要 知其然知其因此然,一塊兒看看ThreadLocal實現原理吧。
3.1 set方法
Set方法應該是ThreadLocal的核心邏輯了。
主要三步:
獲取當前線程
獲取ThreadLocalMap對象
若是ThreadLocalMap對象存在,則將當前線程對象做爲key,要存儲的對象做爲value存到map中 若是ThreadLocalMap對象不存在,就調用creatMap( )進行建立
3.2 ThreadLocalMap是什麼。
ThreadLocalMap是一個定義在ThreadLocal類內部的靜態類,裏面還定義了一個Entry類做爲存儲值的地方。
ThreadLocalMap的key是當前ThreadLocal對象,value是咱們要存儲的值(對象)。
調用creatMap的時候,就是新建一個ThreadLocalMap對象
同時,ThreadLocalMap在Thread類中做爲一個屬性存在。
每一個線程Thread維護了ThreadLocalMap這麼一個Map,這個map的key是LocalThread對象自己,value則是要存儲的對象
3.3 get方法
Get方法就比較簡單了,就是從map中取值的過程。
3.4 ThreadLocal小結
如今,讓咱們從新梳理一遍,看看ThreadLocal是如何實現變量的線程隔離的:
每一個Thread維護着一個ThreadLocalMap的引用
ThreadLocalMap是ThreadLocal的內部類,用Entry來進行存儲,key是ThreadLocal對象,值是傳遞進來的對象
調用ThreadLocal的get()/set()方法時,實際上就是以ThreadLocal對象爲key,在ThreadLocalMap中讀寫value
4.實戰要點
在一開始的優化設計中,不知道你們有沒有注意到對ThreadLocal的remove調用。
這裏就須要談談ThreadLocal使用時的,兩個要點。尤爲是在使用線程池的時候使用ThreadLocal。
4.1 避免內存泄露
在ThreadLocalMap介紹的時候,咱們能夠看到,ThreadLocalMap是Thread的一個屬性。所以,ThreadLocalMap和Thread的生命週期是同樣的。
若是沒有手動刪除對應的ThreadLocal的key,那麼就會形成內存泄漏沒法回收。尤爲在線程池環境下,線程會被不斷複用。
4.2 線程池避免重複線程變量影響
以上文優化案例爲例。
在MVC中,每次請求進來會使用線程池複用線程。若是請求帶了用戶信息,那麼就會重置ThreadLocal對應的用戶信息,若是請求沒有帶用戶信息,必須手動清除一下當前ThreadLocal對應的變量,不然後面使用過程當中可能會形成混亂。
原創:阿丸筆記(微信公衆號:aone_note),歡迎 分享,轉載請保留出處。
掃描下方二維碼能夠關注我哦~
本文分享自微信公衆號 - 阿丸筆記(aone_note)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。