新聞中心
[[344746]]

創(chuàng)新互聯(lián)是一家專業(yè)提供洪澤企業(yè)網(wǎng)站建設(shè),專注與網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)、HTML5建站、小程序制作等業(yè)務(wù)。10年已為洪澤眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)站設(shè)計(jì)公司優(yōu)惠進(jìn)行中。
最近接手了一個(gè)老項(xiàng)目,看到一個(gè)很有意思的現(xiàn)象。
這個(gè)項(xiàng)目中大量的方法入?yún)⒍紩?huì)帶上user信息,比如這樣
它的意圖是希望在方法內(nèi)使用user的信息,但是如此大范圍的傳遞用戶信息,第一感覺就是不優(yōu)雅。那有什么辦法可以優(yōu)化一下呢?
我們第一反應(yīng)是,可以存一個(gè)全局變量,在初始位置將用戶信息存入全局變量,然后在需要的地方去get一下。
那在WEB應(yīng)用中,每個(gè)請求都是一個(gè)獨(dú)立線程,怎么去標(biāo)記呢?
可以用線程的id去作為map的key,將該請求的用戶信息作為map的value。
沒錯(cuò),Java已經(jīng)幫我們封裝好了這么一個(gè)對象,它就是我們今天要說的ThreadLocal。
- 什么是ThreadLocal
- 如何使用ThreadLocal優(yōu)化userid層層傳遞的問題
- ThreadLocal原理是啥
- ThreadLocal的實(shí)戰(zhàn)要點(diǎn)
1.什么是ThreadLocal
先來看下JDK的注釋:
簡單翻譯過來,就是說:
- ThreadLocal提供了線程隔離的局部變量,通過get( )和set( )方法操作當(dāng)前線程對應(yīng)的變量,而且不會(huì)和其他線程沖突,實(shí)現(xiàn)了基于線程的數(shù)據(jù)隔離。
2.如何使用ThreadLocal進(jìn)行優(yōu)化
話不多說,基于我們開頭的例子,我迫不及待地用ThreadLocal來優(yōu)化一下。
2.1 構(gòu)建基于ThreadLocal的上下文
定義一個(gè)SessionUser類,存儲用戶信息,包括用戶id、用戶名。
然后定義一個(gè)基于ThreadLocal的上下文SessionUserContext,代碼如下所示。
2.2 信息存入ThreadLocal中
在我們的優(yōu)化案例中,就是存入用戶信息。
解析請求中的用戶信息有很多方法。本文以HandlerIntercept為例,說明下MVC中的一種方式。
- 實(shí)現(xiàn)HandlerIntercept接口
- 重寫preHandler方法
- 解析HttpServletRequest,獲取用戶信息
- 用戶信息存于SessionUserContext
源碼如下所示。
2.3 在需要的地方獲取信息
原本需要傳入CurrentUser的參數(shù)都可以去掉了。
在需要用戶信息的時(shí)候,直接從SessionUserContext中獲取即可。
哈哈,是不是看起來一下子清爽了很多。
可以在任何地方獲取user信息,不再需要層層傳遞用戶信息了。
3.ThreadLocal實(shí)現(xiàn)原理
上面我們已經(jīng)知道了怎么通過ThreadLocal進(jìn)行優(yōu)化。
下面,我們要 知其然知其所以然,一起看看ThreadLocal實(shí)現(xiàn)原理吧。
3.1 set方法
Set方法應(yīng)該是ThreadLocal的核心邏輯了。
主要三步:
獲取當(dāng)前線程
- 獲取ThreadLocalMap對象
- 如果ThreadLocalMap對象存在,則將當(dāng)前線程對象作為key,要存儲的對象作為value存到map中 如果ThreadLocalMap對象不存在,就調(diào)用creatMap( )進(jìn)行創(chuàng)建
3.2 ThreadLocalMap是什么。
ThreadLocalMap是一個(gè)定義在ThreadLocal類內(nèi)部的靜態(tài)類,里面還定義了一個(gè)Entry類作為存儲值的地方。
ThreadLocalMap的key是當(dāng)前ThreadLocal對象,value是我們要存儲的值(對象)。
調(diào)用creatMap的時(shí)候,就是新建一個(gè)ThreadLocalMap對象
同時(shí),ThreadLocalMap在Thread類中作為一個(gè)屬性存在。
每個(gè)線程Thread維護(hù)了ThreadLocalMap這么一個(gè)Map,這個(gè)map的key是LocalThread對象本身,value則是要存儲的對象
3.3 get方法
Get方法就比較簡單了,就是從map中取值的過程。
3.4 ThreadLocal小結(jié)
現(xiàn)在,讓我們重新梳理一遍,看看ThreadLocal是如何實(shí)現(xiàn)變量的線程隔離的:
每個(gè)Thread維護(hù)著一個(gè)ThreadLocalMap的引用
ThreadLocalMap是ThreadLocal的內(nèi)部類,用Entry來進(jìn)行存儲,key是ThreadLocal對象,值是傳遞進(jìn)來的對象
調(diào)用ThreadLocal的get()/set()方法時(shí),實(shí)際上就是以ThreadLocal對象為key,在ThreadLocalMap中讀寫value
4.實(shí)戰(zhàn)要點(diǎn)
在一開始的優(yōu)化設(shè)計(jì)中,不知道大家有沒有注意到對ThreadLocal的remove調(diào)用。
這里就需要談?wù)凾hreadLocal使用時(shí)的,兩個(gè)要點(diǎn)。尤其是在使用線程池的時(shí)候使用ThreadLocal。
4.1 避免內(nèi)存泄露
在ThreadLocalMap介紹的時(shí)候,我們可以看到,ThreadLocalMap是Thread的一個(gè)屬性。因此,ThreadLocalMap和Thread的生命周期是一樣的。
如果沒有手動(dòng)刪除對應(yīng)的ThreadLocal的key,那么就會(huì)造成內(nèi)存泄漏無法回收。尤其在線程池環(huán)境下,線程會(huì)被不斷復(fù)用。
4.2 線程池避免重復(fù)線程變量影響
以上文優(yōu)化案例為例。
在MVC中,每次請求進(jìn)來會(huì)使用線程池復(fù)用線程。如果請求帶了用戶信息,那么就會(huì)重置ThreadLocal對應(yīng)的用戶信息,如果請求沒有帶用戶信息,必須手動(dòng)清除一下當(dāng)前ThreadLocal對應(yīng)的變量,否則后面使用過程中可能會(huì)造成混亂。
網(wǎng)頁名稱:用ThreadLocal來優(yōu)化下代碼吧
分享網(wǎng)址:http://www.5511xx.com/article/cdpsdjj.html


咨詢
建站咨詢
