最近接手了一個老項目,看到一個頗有意思的現象。git
這個項目中大量的方法入參都會帶上user信息,好比這樣github
它的意圖是但願在方法內使用user的信息,可是如此大範圍的傳遞用戶信息,第一感受就是不優雅。那有什麼辦法能夠優化一下呢?面試
咱們第一反應是,能夠存一個全局變量,在初始位置將用戶信息存入全局變量,而後在須要的地方去get一下。微信
那在WEB應用中,每一個請求都是一個獨立線程,怎麼去標記呢?學習
能夠用線程的id去做爲map的key,將該請求的用戶信息做爲map的value。優化
咦?很熟悉的感受。線程
沒錯,Java已經幫咱們封裝好了這麼一個對象,它就是咱們今天要說的ThreadLocal。翻譯
先來看下JDK的註釋:設計
簡單翻譯過來,就是說:3d
ThreadLocal提供了線程隔離的局部變量,經過get( )和set( )方法操做當前線程對應的變量,並且不會和其餘線程衝突,實現了基於線程的數據隔離。
話很少說,基於咱們開頭的例子,我火燒眉毛地用ThreadLocal來優化一下。
定義一個SessionUser類,存儲用戶信息,包括用戶id、用戶名。
而後定義一個基於ThreadLocal的上下文SessionUserContext,代碼以下所示。
在咱們的優化案例中,就是存入用戶信息。
解析請求中的用戶信息有不少方法。本文以HandlerIntercept爲例,說明下MVC中的一種方式。
源碼以下所示。
本來須要傳入CurrentUser的參數均可以去掉了。
在須要用戶信息的時候,直接從SessionUserContext中獲取便可。
哈哈,是否是看起來一會兒清爽了不少。
能夠在任何地方獲取user信息,再也不須要層層傳遞用戶信息了。
上面咱們已經知道了怎麼經過ThreadLocal進行優化。
下面,咱們要 知其然知其因此然,一塊兒看看ThreadLocal實現原理吧。
Set方法應該是ThreadLocal的核心邏輯了。
主要三步:
ThreadLocalMap是一個定義在ThreadLocal類內部的靜態類,裏面還定義了一個Entry類做爲存儲值的地方。
ThreadLocalMap的key是當前ThreadLocal對象,value是咱們要存儲的值(對象)。
調用creatMap的時候,就是新建一個ThreadLocalMap對象
同時,ThreadLocalMap在Thread類中做爲一個屬性存在。
每一個線程Thread維護了ThreadLocalMap這麼一個Map,這個map的key是LocalThread對象自己,value則是要存儲的對象
Get方法就比較簡單了,就是從map中取值的過程。
如今,讓咱們從新梳理一遍,看看ThreadLocal是如何實現變量的線程隔離的:
在一開始的優化設計中,不知道你們有沒有注意到對ThreadLocal的remove調用。
這裏就須要談談ThreadLocal使用時的,兩個要點。尤爲是在使用線程池的時候使用ThreadLocal。
在ThreadLocalMap介紹的時候,咱們能夠看到,ThreadLocalMap是Thread的一個屬性。所以,ThreadLocalMap和Thread的生命週期是同樣的。
若是沒有手動刪除對應的ThreadLocal的key,那麼就會形成內存泄漏沒法回收。尤爲在線程池環境下,線程會被不斷複用。
之前文優化案例爲例。
在MVC中,每次請求進來會使用線程池複用線程。若是請求帶了用戶信息,那麼就會重置ThreadLocal對應的用戶信息,若是請求沒有帶用戶信息,必須手動清除一下當前ThreadLocal對應的變量,不然後面使用過程當中可能會形成混亂。
都看到最後了,原創不易,點個關注,點個贊吧~
文章持續更新,能夠微信搜索「阿丸筆記 」第一時間閱讀,回覆關鍵字【學習】有我準備的一線大廠面試資料。
知識碎片從新梳理,構建Java知識圖譜: github.com/saigu/JavaK…(歷史文章查閱很是方便)