從獲取當(dāng)前用戶到Security再到ThreadLocal(二)

以前覺(jué)得魯迅先生的“我家門口有兩棵樹(shù)一棵是棗樹(shù),另一棵也是棗樹(shù)”完完全全是在湊字?jǐn)?shù)。不過(guò)最近把這個(gè)句式一帶入到“我的賬戶里有兩只股票,一只是綠的霹陡,另一只也是綠的”,馬上就懂了止状,不愧是大文豪烹棉,悲憫的氛圍感一下就出來(lái)了。

上文介紹了security的用戶信息保存在ThreadLocal中导俘,那我們這篇文章聊下ThreadLocal

上節(jié)回顧

??上篇文章主要是從具體實(shí)現(xiàn)層面來(lái)分析峦耘,基于源碼和調(diào)用邏輯的分析聚焦于‘術(shù)’,我們?cè)購(gòu)摹ā膶用媛帽。瑩Q個(gè)視角看下為什么要這么做辅髓,加深理解;
??我們部署的web應(yīng)用少梁,從tomcat——>security——>Ctrl——>Serveice——>Dao洛口,我們需要用到用戶信息的地方很多,如果換做你自己想法實(shí)現(xiàn)
1凯沪、最簡(jiǎn)單的就是透?jìng)鞯谘妫瑥念^到尾的方法都加上User這個(gè)對(duì)象,需要時(shí)獲取妨马,這樣做的缺點(diǎn)顯而易見(jiàn):

  • 你不知道哪些方法要使用用戶信息挺举,為了后面萬(wàn)一需要使用,得把所有方法的入?yún)⒁还赡X的加上
  • 寫業(yè)務(wù)代碼的人還要關(guān)注這個(gè)方法需不需要獲取用戶信息烘跺,導(dǎo)致所有接口入?yún)⑷哂?/li>
  • 后期修改變更極為痛苦

2湘纵、統(tǒng)一存儲(chǔ),然后提供工具類獲取滤淳,這種方法比上一個(gè)優(yōu)雅梧喷,但是存在一個(gè)問(wèn)題是多個(gè)線程訪問(wèn)同一個(gè)資源,存在竟態(tài)條件,會(huì)有線程安全問(wèn)題铺敌,那就得加鎖汇歹,上鎖的話性能就不行了

3、如果可以將用戶信息進(jìn)行本地化存儲(chǔ)偿凭,讓每個(gè)線程都有屬于自己的本地資源产弹,避免多線程之間的共享。這樣ThreadLocal就應(yīng)運(yùn)而生

ThreadLocal初介紹

我們先看Thread類

public class Thread implements Runnable {
    ···
    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */(開(kāi)始學(xué)英語(yǔ)了笔喉,與此線程相關(guān)的線程本地值取视。這map 是由ThreadLocal 類進(jìn)行維護(hù))
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.(與此線程相關(guān)的InheritableThreadLocal值硝皂。這map 是由InheritableThreadLocal 類進(jìn)行維護(hù))
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    ···

??可以看到ThreadLocalMap 是Thread類的一個(gè)屬性常挚;但是這些屬性ThreadLocal有啥關(guān)系,怎么使用稽物,我們先擱置奄毡,把后面看了再來(lái)解決


image.png

??可以看到ThreadLocalMap是ThreadLocal的內(nèi)部類,然后該內(nèi)部類里面又存在一個(gè)內(nèi)部類Entry贝或,這個(gè)Entry理念和HashMap的Entry類似吼过;可以看到ThreadLocalMap里面把我們需要存儲(chǔ)的東西放到Entry[]數(shù)組table里面,即我們的用戶信息是放到一個(gè)對(duì)象數(shù)組里面的咪奖,最終存儲(chǔ)在Object value這個(gè)屬性中盗忱;

我把上面這段話再捋一捋,把整個(gè)過(guò)程翻譯下:
我們保存用戶信息的時(shí)候羊赵,在security里面是運(yùn)行的代碼是 contextHolder.set(context); contextHolder就是一個(gè)threadLocal 對(duì)象趟佃,這個(gè)然后contextHolder調(diào)用的set方法如下:

public void set(T value) {
       //獲取到當(dāng)前線程
       Thread t = Thread.currentThread();
       //根據(jù)線程獲取當(dāng)前線程的ThreadLocalMap對(duì)象
       ThreadLocalMap map = getMap(t);
       if (map != null)
           //設(shè)置進(jìn)去的key為ThreadLocal; value為我們想要保存的用戶數(shù)據(jù)
           map.set(this, value);
       else
           createMap(t, value);
   }
ThreadLocalMap getMap(Thread t) {
       return t.threadLocals;
   }
public T get() {
       //獲取到當(dāng)前線程
       Thread t = Thread.currentThread();
       //根據(jù)線程獲取當(dāng)前線程的ThreadLocalMap對(duì)象
       ThreadLocalMap map = getMap(t);
       if (map != null) {
            //this就是指代當(dāng)前的這個(gè)threadlocal對(duì)象
           ThreadLocalMap.Entry e = map.getEntry(this);
           if (e != null) {
               @SuppressWarnings("unchecked")
               T result = (T)e.value;
               return result;
           }
       }
       //找不到的情況下昧捷,調(diào)用初始化方法闲昭,返回初始值
       return setInitialValue();
   }
ThreadLocalMap getMap(Thread t) {
       return t.threadLocals;
   }

ThreadLocal深入

弱引用&內(nèi)存泄露

ThreadLocalMap源碼圖.png

??從圖上可知,Entry是繼承了弱引用的類靡挥,但是Entry本身不是弱引用序矩,里面那個(gè)key才是,將key(即當(dāng)前threadlocal對(duì)象)存在當(dāng)前父類的referent屬性中跋破,value存放我們想要存儲(chǔ)的值簸淀;


threadlocal的引用關(guān)系圖.png(網(wǎng)圖,侵刪)
  • 什么情況下會(huì)存在內(nèi)存泄露毒返?
    ??拿線程池舉例:線程池中的線程生命周期比較長(zhǎng)租幕,線程引用到線程對(duì)象的強(qiáng)引用鏈會(huì)一直存在,但是圖片上面那條threadlocal的引用隨著方法的出棧后饿悬,該條強(qiáng)引用鏈就沒(méi)有了令蛉,這一時(shí)刻分析下threadlocal的
    的引用鏈路,只存在一條了,就是放入到Entry-key里面存在一條弱引用鏈珠叔,然后發(fā)生gc蝎宇,由于key是弱引用,所以
    threadLocal 對(duì)象會(huì)被回收祷安,這個(gè)時(shí)候Entry里面的key沒(méi)了姥芥,沒(méi)有key,那value肯定訪問(wèn)不到汇鞭,這個(gè)時(shí)候Entry對(duì)象占用的內(nèi)存區(qū)域無(wú)法被釋放凉唐,造成內(nèi)存泄露。
  • 那為啥要設(shè)計(jì)為弱引用而不是強(qiáng)引用呢霍骄?
    ??這個(gè)是沒(méi)有明確答案的台囱,只能自己揣摩設(shè)計(jì)思路,我的理解是threadlocal放入到Entry里面的key如果是強(qiáng)引用读整,使用完了簿训,開(kāi)發(fā)人員沒(méi)有釋放,會(huì)造成更大的內(nèi)存泄露米间,兩權(quán)相害取其輕强品,既然都會(huì)泄露,所以稍微選擇影響小點(diǎn)的屈糊,所以改為弱引用的榛;另外代碼里面多處:set,擴(kuò)容等都涉及清除掉Entry key為空的情況逻锐,反向印證了作者是知道這一點(diǎn)夫晌,且為之做了彌補(bǔ)措施

擴(kuò)容

@TODO

Q & A

Q:為什么threadlocalMap要設(shè)計(jì)到Thread線程里面,而不是直接將threadlocal設(shè)計(jì)為一個(gè)公用的map谦去,map-key為線程對(duì)象慷丽,map-value為我們想存儲(chǔ)的值,豈不美哉鳄哭?
A:這樣多個(gè)線程訪問(wèn)threadlocalMap要糊,就又回到集合下Map的思路了,會(huì)產(chǎn)生線程安全問(wèn)題妆丘,同時(shí)加鎖會(huì)導(dǎo)致性能損耗锄俄;


Q:為什么value不為弱引用?
A:Entry中的value如果弱引用勺拣,那么只要GC奶赠,value就會(huì)被清理掉,這時(shí)如果通過(guò)threadlocal對(duì)象查對(duì)應(yīng)的值药有,是null毅戈;


Q:threadlocal有什么好處苹丸?
A:一是可以避免競(jìng)爭(zhēng),各自有一份副本苇经;二是可以方便線程傳參赘理,比如登錄用戶信息;


Q:threadlocal使用場(chǎng)景扇单?
A:保存數(shù)據(jù)庫(kù)鏈接商模;如果一個(gè)請(qǐng)求中涉及多個(gè) DAO 操作,而如果這些DAO中的Connection都是獨(dú)立的話蜘澜,就沒(méi)有辦法完成一個(gè)事務(wù)施流。但是如果DAO 中的 Connection 是從 ThreadLocal 中獲得的(意味著都是同一個(gè)對(duì)象), 那么這些 DAO 就會(huì)被納入到同一個(gè) Connection 之下鄙信。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瞪醋,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子扮碧,更是在濱河造成了極大的恐慌趟章,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慎王,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡宏侍,警方通過(guò)查閱死者的電腦和手機(jī)赖淤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)谅河,“玉大人咱旱,你說(shuō)我怎么就攤上這事”了#” “怎么了吐限?”我有些...
    開(kāi)封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)褂始。 經(jīng)常有香客問(wèn)我诸典,道長(zhǎng),這世上最難降的妖魔是什么崎苗? 我笑而不...
    開(kāi)封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任狐粱,我火速辦了婚禮,結(jié)果婚禮上胆数,老公的妹妹穿的比我還像新娘肌蜻。我一直安慰自己,他們只是感情好必尼,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布蒋搜。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪豆挽。 梳的紋絲不亂的頭發(fā)上酸休,一...
    開(kāi)封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音祷杈,去河邊找鬼斑司。 笑死,一個(gè)胖子當(dāng)著我的面吹牛但汞,可吹牛的內(nèi)容都是我干的宿刮。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼私蕾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼僵缺!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起踩叭,我...
    開(kāi)封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤磕潮,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后容贝,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體自脯,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年斤富,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了膏潮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡满力,死狀恐怖焕参,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情油额,我是刑警寧澤叠纷,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站潦嘶,受9級(jí)特大地震影響涩嚣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜衬以,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一缓艳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧看峻,春花似錦阶淘、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)坤塞。三九已至,卻和暖如春澈蚌,著一層夾襖步出監(jiān)牢的瞬間摹芙,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工宛瞄, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留浮禾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓份汗,卻偏偏與公主長(zhǎng)得像盈电,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子杯活,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • 問(wèn)題背景 最近看到群里面有人提問(wèn):我想要在DAO層保存數(shù)據(jù)時(shí)匆帚,獲取到當(dāng)前登錄用戶,但是我又不想去做token換用戶...
    新生代民工代表閱讀 857評(píng)論 0 0
  • 一旁钧、ThreadLocal 適合用在哪些實(shí)際生產(chǎn)的場(chǎng)景中 保存每個(gè)線程獨(dú)享的對(duì)象為每個(gè)線程都創(chuàng)建一個(gè)副本吸重,這樣每個(gè)...
    Travis_Wu閱讀 243評(píng)論 0 1
  • 一、ThreadLocal介紹 ThreadLocal是用來(lái)維護(hù)本線程的變量,為每一個(gè)線程分配一個(gè)只屬于該線程的對(duì)...
    shuaidong閱讀 1,596評(píng)論 0 7
  • 一歪今、關(guān)于線程本地存儲(chǔ) 線程本地存儲(chǔ)是一種自動(dòng)化機(jī)制嚎幸,可以為使用相同變量的每個(gè)不同的線程都創(chuàng)建不同的存儲(chǔ),通過(guò)根除對(duì)...
    hu1991die閱讀 3,633評(píng)論 0 2
  • 1彤委、概述 ThreadLocal`為解決多線程程序的并發(fā)問(wèn)題提供了一種新的思路鞭铆。使用這個(gè)工具類可以很簡(jiǎn)潔地編寫出優(yōu)...
    冰河winner閱讀 420評(píng)論 0 0