




下載本文檔
版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】Android進(jìn)階你必須要了解的知識(shí):ThreadLocal
1、ThreadLocal是什么?ThreadLocal是一個(gè)線程內(nèi)部數(shù)據(jù)存儲(chǔ)類(lèi),通過(guò)他可以在指定的線程中存儲(chǔ)數(shù)據(jù)。存儲(chǔ)后,只能在指定的線程中獲取到存儲(chǔ)的數(shù)據(jù),對(duì)其他線程來(lái)說(shuō)無(wú)法獲取到數(shù)據(jù)。2、ThreadLocal的使用場(chǎng)景日常使用場(chǎng)景不多,當(dāng)某些數(shù)據(jù)是以線程為作用域并且不同線程具有不同的數(shù)據(jù)副本的時(shí)候,可以考慮使用ThreadLocal。Android源碼的Lopper、ActivityThread以及AMS中都用到了ThreadLocal。3、ThreadLocal的使用示例publicclassThreadLocalActivityextendsAppCompatActivity{
privateThreadLocal<String>name=newThreadLocal<>();
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thread_local);
name.set("小明");
Log.d("ThreadLocalActivity","Thread:"+Thread.currentThread().getName()+"name:"+name.get());
newThread("thread1"){
@Override
publicvoidrun(){
name.set("小紅");
Log.d("ThreadLocalActivity","Thread:"+Thread.currentThread().getName()+"name:"+name.get());
}
}.start();
newThread("thread2"){
@Override
publicvoidrun(){
Log.d("ThreadLocalActivity","Thread:"+Thread.currentThread().getName()+"name:"+name.get());
}
}.start();
}
}運(yùn)行結(jié)果:D/ThreadLocalActivity:Thread:mainname:小明
D/ThreadLocalActivity:Thread:thread1name:小紅
D/ThreadLocalActivity:Thread:thread2name:null可以看到雖然訪問(wèn)的是同一個(gè)ThreadLocal對(duì)象,但是獲取到的值卻是不一樣的。4、ThreadLocal的源碼閱讀那么為什么會(huì)造成這樣的結(jié)果呢?這就需要去看看ThreadLocal的源碼實(shí)現(xiàn),這里的源碼版本為API28。主要看它的get和set方法。set方法:publicvoidset(Tvalue){
Threadt=Thread.currentThread();
ThreadLocalMapmap=getMap(t);
if(map!=null)
map.set(this,value);
else
createMap(t,value);
}set方法中首先獲取了當(dāng)前線程對(duì)象,然后通過(guò)getMap方法傳入當(dāng)前線程t獲取到一個(gè)ThreadLocalMap,接下來(lái)判斷這個(gè)map是否為空,不為空就直接將當(dāng)前ThreadLocal作為key,set方法中傳入要保存的值最為value,存放到map中;如果map為空就調(diào)用createMap方法創(chuàng)建一個(gè)map并同樣將當(dāng)前ThreadLocal和要保存的值作為key和value加入到map中。接下先看getMap方法:ThreadLocalMapgetMap(Threadt){
returnt.threadLocals;
}getMap方法比較簡(jiǎn)單,就是返回從傳入的當(dāng)前線程對(duì)象的成員變量threadLocals。接著是createMap方法:voidcreateMap(Threadt,TfirstValue){
t.threadLocals=newThreadLocalMap(this,firstValue);
}createMap方法也很簡(jiǎn)單就是new了一個(gè)ThreadLocalMap并賦給當(dāng)前線程對(duì)象t中的threadLocals。原來(lái)這個(gè)Map是存放在Thread類(lèi)中的。于是進(jìn)入Thread類(lèi)中查看。Thread.java第188-190行:/*ThreadLocalvaluespertainingtothisthread.Thismapismaintained
*bytheThreadLocalclass.*/
ThreadLocal.ThreadLocalMapthreadLocals=null;根據(jù)這里的注釋可以得知,每個(gè)線程Thread中都有一個(gè)ThreadLocalMap類(lèi)型的threadLocals成員變量來(lái)保存數(shù)據(jù),通過(guò)ThreadLocal類(lèi)來(lái)進(jìn)行維護(hù)。這樣看來(lái)我們每次在不同線程調(diào)用ThreadLocal的set方法set的數(shù)據(jù)是存在不同線程的ThreadLocalMap中的,就像注釋說(shuō)的ThreadLocal只是起了個(gè)維護(hù)ThreadLocalMap的功能。想到是get方法同樣也是到不同線程的ThreadLocalMap去取數(shù)據(jù)。get方法:publicTget(){
Threadt=Thread.currentThread();
ThreadLocalMapmap=getMap(t);
if(map!=null){
ThreadLocalMap.Entrye=map.getEntry(this);
if(e!=null){
@SuppressWarnings("unchecked")
Tresult=(T)e.value;
returnresult;
}
}
returnsetInitialValue();
}果然,get方法中同樣是先獲取當(dāng)前線程對(duì)象,然后在拿著這個(gè)對(duì)象t去獲取到t中的ThreadLocalMap,只要map不等于null就調(diào)用map.getEntry(this)方法來(lái)獲取數(shù)據(jù),因?yàn)門(mén)hreadLocalMap里使用一個(gè)內(nèi)部類(lèi)Entry來(lái)存儲(chǔ)數(shù)據(jù)的,所以調(diào)用getEntry(this)方法,傳入的key是當(dāng)前的ThreadLocal。這樣獲取到Entry類(lèi)型數(shù)據(jù)e,只要e不為null,返回e.value即先前存儲(chǔ)的數(shù)據(jù)。如果獲取到的map為null又或者根據(jù)key獲取Entry為null,就調(diào)用setInitialValue方法初始化一個(gè)value返回。setInitialValue和initialValue方法:privateTsetInitialValue(){
Tvalue=initialValue();
Threadt=Thread.currentThread();
ThreadLocalMapmap=getMap(t);
if(map!=null)
map.set(this,value);
else
createMap(t,value);
returnvalue;
}
protectedTinitialValue(){
returnnull;
}setInitialValue方法中首先調(diào)用initialValue方法初始化了一個(gè)空value,之后的操作和set方法相同,將這個(gè)空的value加入到當(dāng)前線程的ThreadLocalMap中去,ThreadLocalMap為空就創(chuàng)建個(gè)Map,最后返回這個(gè)空值。至此,ThreadLocal的get、set方法就都看過(guò)了,也理解了ThreadLocal可以在多個(gè)線程中操作而互不干擾的原因。但是ThreadLocal還有一個(gè)要注意的地方就是ThreadLocal使用不當(dāng)會(huì)造成內(nèi)存泄漏。5、ThreadLocal內(nèi)存泄漏的原因內(nèi)存泄漏的根本原因是當(dāng)一個(gè)對(duì)象已經(jīng)不需要再使用本該被回收時(shí),另外一個(gè)正在使用的對(duì)象持有它的引用從而導(dǎo)致它不能被回收,導(dǎo)致本該被回收的對(duì)象不能被回收而停留在堆內(nèi)存中。那么ThreadLocal中是在哪里發(fā)生的呢?這就要看到ThreadLocalMap中存儲(chǔ)數(shù)據(jù)的內(nèi)部類(lèi)Entry。staticclassEntryextendsWeakReference<ThreadLocal<?>>{
/**ThevalueassociatedwiththisThreadLocal.*/
Objectvalue;
Entry(ThreadLocal<?>k,Objectv){
super(k);
value=v;
}
}可以看到這個(gè)Entry類(lèi),這里的key是使用了個(gè)弱引用,所以因?yàn)槭褂萌跻眠@里的key,ThreadLocal會(huì)在JVM下次GC回收時(shí)候被回收,而造成了個(gè)key為null的情況,而外部ThreadLocalMap是沒(méi)辦法通過(guò)nullkey來(lái)找到對(duì)應(yīng)value的。如果當(dāng)前線程一直在運(yùn)行,那么線程中的ThreadLocalMap也就一直存在,而map中卻存在key已經(jīng)被回收為null對(duì)應(yīng)的Entry和value卻一直存在不會(huì)被回收,造成內(nèi)存的泄漏。不過(guò),這一點(diǎn)設(shè)計(jì)者也考慮到了,在get()、set()、remove()方法調(diào)用的時(shí)候會(huì)清除掉線程ThreadLocalMap中所有Entry中Key為null的Value,并將整個(gè)Entry設(shè)置為null,這樣在下次回收時(shí)就能將Entry和value回收。這樣看上去好像是因?yàn)閗ey使用了弱引用才導(dǎo)致的內(nèi)存泄漏,為了解決還特意添加了清除nullkey的功能,那么是不是不用弱引用就可以了呢?很顯然不是這樣的。設(shè)計(jì)者使用弱引用是由原因的。所以,由于ThreadLocalMap和線程Thread的生命周期一樣長(zhǎng),如果沒(méi)有手動(dòng)刪除Map的中的key,無(wú)論使用強(qiáng)引用還是弱引用實(shí)際上都會(huì)出現(xiàn)內(nèi)存泄漏,
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 汽車(chē)知識(shí)報(bào)告范文
- 二零二五年度碎石場(chǎng)安全生產(chǎn)監(jiān)管服務(wù)合同
- 二零二五年度屋頂光伏電站委托租賃管理合同
- 宜昌水電工程安全風(fēng)險(xiǎn)評(píng)估與應(yīng)急預(yù)案編制合同(2025年度)
- 二零二五年度遺產(chǎn)繼承遺產(chǎn)分割協(xié)議
- 二零二五年度智能化沙石運(yùn)輸管理服務(wù)協(xié)議
- 2025年度離婚后子女學(xué)業(yè)支持協(xié)議
- 二零二五年度家教兼職合同與家長(zhǎng)溝通規(guī)范
- 二零二五年度高校畢業(yè)生就業(yè)實(shí)習(xí)補(bǔ)貼結(jié)算跟蹤三方協(xié)議書(shū)
- 二零二五年度高端定制服裝品牌區(qū)域代理合作協(xié)議
- 2024年度供應(yīng)商管理培訓(xùn)課件
- 6、水平四+田徑18課時(shí)大單元計(jì)劃-《雙手頭上前擲實(shí)心球》
- 《審計(jì)課件東北財(cái)經(jīng)大學(xué)會(huì)計(jì)系列教材》課件
- 中國(guó)老年危重患者營(yíng)養(yǎng)支持治療指南2023解讀課件
- 《管理信息系統(tǒng)》考試復(fù)習(xí)題庫(kù)(含答案)
- 《光伏電站運(yùn)行與維護(hù)》試題及答案一
- 人教版初中全部英語(yǔ)單詞表
- 2024年浙江省中考社會(huì)試卷真題(含標(biāo)準(zhǔn)答案及評(píng)分標(biāo)準(zhǔn))
- 2024年貴州省高職(專(zhuān)科)分類(lèi)考試招收中職畢業(yè)生文化綜合考試語(yǔ)文試題
- 一年級(jí)體育教案全冊(cè)(水平一)下冊(cè)
- 全身麻醉后護(hù)理常規(guī)
評(píng)論
0/150
提交評(píng)論