一、什么是ThreadLocal
本人英文能力有限侧纯,翻譯不對(duì)骚揍,望勿噴
ThreadLocal提供了線程的局部變量媳拴,每個(gè)線程都可以通過(guò)set()和get()來(lái)對(duì)這個(gè)局部變量進(jìn)行操作依鸥,但不會(huì)和其他線程的局部變量進(jìn)行沖突亥至,實(shí)現(xiàn)了線程的數(shù)據(jù)隔離~。
簡(jiǎn)要言之:往ThreadLocal中填充的變量屬于當(dāng)前線程贱迟,該變量對(duì)其他線程而言是隔離的抬闯。
二、為什么要學(xué)習(xí)ThreadLocal关筒?
2.1管理Connection
最典型的是管理數(shù)據(jù)庫(kù)的Connection:當(dāng)時(shí)在學(xué)JDBC的時(shí)候,為了方便操作寫(xiě)了一個(gè)簡(jiǎn)單數(shù)據(jù)庫(kù)連接池杯缺,需要數(shù)據(jù)庫(kù)連接池的理由也很簡(jiǎn)單蒸播,頻繁創(chuàng)建和關(guān)閉Connection是一件非常耗費(fèi)資源的操作,因此需要?jiǎng)?chuàng)建數(shù)據(jù)庫(kù)連接池~
那么萍肆,數(shù)據(jù)庫(kù)連接池的連接怎么管理呢袍榆??我們交由ThreadLocal來(lái)進(jìn)行管理塘揣。為什么交給它來(lái)管理呢包雀??ThreadLocal能夠?qū)崿F(xiàn)當(dāng)前線程的操作都是用同一個(gè)Connection亲铡,保證了事務(wù)才写!
2.2避免一些參數(shù)傳遞
而如果用了ThreadLocal的話,ThreadLocal就相當(dāng)于一個(gè)機(jī)構(gòu)奖蔓,ThreadLocal機(jī)構(gòu)做了記錄你有那么多張證件赞草。用到的時(shí)候就不用自己掏了,問(wèn)機(jī)構(gòu)拿就可以了吆鹤。
在咨詢(xún)時(shí)的時(shí)候就告訴機(jī)構(gòu):來(lái)厨疙,把我的身份證、房產(chǎn)證疑务、學(xué)生證通通給他沾凄。在辦理時(shí)又告訴機(jī)構(gòu):來(lái)梗醇,把我的身份證、房產(chǎn)證撒蟀、學(xué)生證通通給他
三叙谨、ThreadLocal實(shí)現(xiàn)的原理
首先,我們來(lái)看一下ThreadLocal的set()方法牙肝,因?yàn)槲覀円话闶褂枚际莕ew完對(duì)象唉俗,就往里邊set對(duì)象了
上面有個(gè)ThreadLocalMap,我們?nèi)タ纯催@是什么配椭?
通過(guò)上面我們可以發(fā)現(xiàn)的是ThreadLocalMap是ThreadLocal的一個(gè)內(nèi)部類(lèi)虫溜。用Entry類(lèi)來(lái)進(jìn)行存儲(chǔ)
我們的值都是存儲(chǔ)到這個(gè)Map上的,key是當(dāng)前ThreadLocal對(duì)象股缸!
如果該Map不存在衡楞,則初始化一個(gè):
每個(gè)Thread維護(hù)著一個(gè)ThreadLocalMap的引用
ThreadLocalMap是ThreadLocal的內(nèi)部類(lèi),用Entry來(lái)進(jìn)行存儲(chǔ)
調(diào)用ThreadLocal的set()方法時(shí)敦姻,實(shí)際上就是往ThreadLocalMap設(shè)置值瘾境,key是ThreadLocal對(duì)象,值是傳遞進(jìn)來(lái)的對(duì)象
調(diào)用ThreadLocal的get()方法時(shí)镰惦,實(shí)際上就是往ThreadLocalMap獲取值迷守,key是ThreadLocal對(duì)象
ThreadLocal本身并不存儲(chǔ)值,它只是作為一個(gè)key來(lái)讓線程從ThreadLocalMap獲取value旺入。
正因?yàn)檫@個(gè)原理兑凿,所以ThreadLocal能夠?qū)崿F(xiàn)“數(shù)據(jù)隔離”,獲取當(dāng)前線程的局部變量值茵瘾,不受其他線程影響~
四礼华、避免內(nèi)存泄露
ThreadLocal內(nèi)存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一樣長(zhǎng),如果沒(méi)有手動(dòng)刪除對(duì)應(yīng)key就會(huì)導(dǎo)致內(nèi)存泄漏拗秘,而不是因?yàn)槿跻谩?/p>
想要避免內(nèi)存泄露就要手動(dòng)remove()掉圣絮!
五、應(yīng)用場(chǎng)景
還記得Hibernate的session獲取場(chǎng)景嗎雕旨?
為什么扮匠?每個(gè)線程訪問(wèn)數(shù)據(jù)庫(kù)都應(yīng)當(dāng)是一個(gè)獨(dú)立的Session會(huì)話,如果多個(gè)線程共享同一個(gè)Session會(huì)話凡涩,有可能其他線程關(guān)閉連接了餐禁,當(dāng)前線程再執(zhí)行提交時(shí)就會(huì)出現(xiàn)會(huì)話已關(guān)閉的異常,導(dǎo)致系統(tǒng)異常突照。此方式能避免線程爭(zhēng)搶Session帮非,提高并發(fā)下的安全性。
使用ThreadLocal的典型場(chǎng)景正如上面的數(shù)據(jù)庫(kù)連接管理,線程會(huì)話管理等場(chǎng)景末盔,只適用于獨(dú)立變量副本的情況筑舅,如果變量為全局共享的,則不適用在高并發(fā)下使用陨舱。