版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
Thespringofmulti-thread—線程安全另一種解決思路第一頁(yè),共27頁(yè)。我需要一個(gè)可靠的單例模式!!上堂回顧第二頁(yè),共27頁(yè)。public
classInnDao{private
volatile
staticInnDaoinstance;privateInnDao(){}public
staticInnDaogetInstance(){if(instance==null){synchronized(InnDao.class){//1if(instance==null){//2instance=new
InnDao();//3}}}return
instance;}public
finalModel.Finder<Integer,Inn>finder=newModel.Finder<Integer,Inn>(Integer.class,Inn.class);}關(guān)于Volatile與同步操作共同使用的思考第三頁(yè),共27頁(yè)。publicclassThreadClientextendsThread{privatestaticintcount;publicvoidrun(){for(inti=0;i<3;i++){System.out.println("當(dāng)前線程名:"+Thread.currentThread().getName()+"count="+count);count++;}}publicstaticvoidmain(String[]args){ThreadClienttc1=newThreadClient();ThreadClienttc2=newThreadClient();ThreadClienttc3=newThreadClient();tc1.start();tc2.start();tc3.start();}}當(dāng)前線程名:Thread-1count=0當(dāng)前線程名:Thread-1count=1當(dāng)前線程名:Thread-1count=2當(dāng)前線程名:Thread-2count=3當(dāng)前線程名:Thread-2count=4當(dāng)前線程名:Thread-2count=5當(dāng)前線程名:Thread-0count=6當(dāng)前線程名:Thread-0count=7當(dāng)前線程名:Thread-0count=8第四頁(yè),共27頁(yè)。publicclassThreadClientextendsThread{privatestaticThreadLocal<Integer>count=newThreadLocal<>();publicThreadClient(Integervalue){count.set(value);}publicvoidrun(){for(inti=0;i<3;i++){System.out.println("當(dāng)前線程名:"+Thread.currentThread().getName()+"count="+count.get());count.set(count.get()+1);try{TimeUnit.SECONDS.sleep(1);}catch(InterruptedExceptione){e.printStackTrace();}}}publicstaticvoidmain(String[]args){ThreadClienttc1=newThreadClient(0);ThreadClienttc2=newThreadClient(0);ThreadClienttc3=newThreadClient(0);tc1.start();tc2.start();tc3.start();}}Exceptioninthread"Thread-1"java.lang.NullPointerException atcom.fanqie.oms.thread.ThreadLocal.ThreadClient.run(ThreadClient.java:21)Exceptioninthread"Thread-0"java.lang.NullPointerException atcom.fanqie.oms.thread.ThreadLocal.ThreadClient.run(ThreadClient.java:21)當(dāng)前線程名:Thread-1count=null當(dāng)前線程名:Thread-0count=nullExceptioninthread"Thread-2"java.lang.NullPointerException atcom.fanqie.oms.thread.ThreadLocal.ThreadClient.run(ThreadClient.java:21)當(dāng)前線程名:Thread-2count=null第五頁(yè),共27頁(yè)。publicclassThreadClientextendsThread{privatestaticThreadLocal<Integer>count=newThreadLocal<Integer>(){@OverrideprotectedIntegerinitialValue(){return0;}};publicvoidrun(){for(inti=0;i<3;i++){System.out.println("當(dāng)前線程名:"+Thread.currentThread().getName()+"count="+count.get());count.set(count.get()+1);try{TimeUnit.SECONDS.sleep(1);}catch(InterruptedExceptione){e.printStackTrace();}}}publicstaticvoidmain(String[]args){ThreadClienttc1=newThreadClient();ThreadClienttc2=newThreadClient();ThreadClienttc3=newThreadClient();tc1.start();tc2.start();tc3.start();}}當(dāng)前線程名:Thread-0count=0當(dāng)前線程名:Thread-1count=0當(dāng)前線程名:Thread-2count=0當(dāng)前線程名:Thread-0count=1當(dāng)前線程名:Thread-2count=1當(dāng)前線程名:Thread-1count=1當(dāng)前線程名:Thread-0count=2當(dāng)前線程名:Thread-2count=2當(dāng)前線程名:Thread-1count=2第六頁(yè),共27頁(yè)。TimeUnitpublicvoidtest(Stringargs[])throwsInterruptedException{System.out.println("Sleepingfor4minutesusingThread.sleep()");Thread.sleep(4*60*1000);System.out.println("Sleepingfor4minutesusingTimeUnitsleep()");TimeUnit.SECONDS.sleep(4);TimeUnit.MINUTES.sleep(4);TimeUnit.HOURS.sleep(1);TimeUnit.DAYS.sleep(1);}第七頁(yè),共27頁(yè)。ThreadLocal是什么
當(dāng)使用ThreadLocal維護(hù)變量時(shí),ThreadLocal為每個(gè)使用該變量的線程提供獨(dú)立的變量副本,所以每一個(gè)線程都可以獨(dú)立地改變自己的副本,而不會(huì)影響其它線程所對(duì)應(yīng)的副本。對(duì)外提供的方法很簡(jiǎn)單publicTget();publicvoidset(Tvalue);publicvoidremove();有利于jvm回收protectedTinitialValue();返回null第八頁(yè),共27頁(yè)。ThreadLocal并不能替代同步機(jī)制,兩者面向的問(wèn)題領(lǐng)域不同。同步機(jī)制是為了同步多個(gè)線程對(duì)相同資源的并發(fā)訪問(wèn),是為了多個(gè)線程之間進(jìn)行通信的有效方式;而ThreadLocal是隔離多個(gè)線程的數(shù)據(jù)共享,從根本上就不在多個(gè)線程之間共享資源(變量),這樣當(dāng)然不需要對(duì)多個(gè)線程進(jìn)行同步了。所以,如果你需要進(jìn)行多個(gè)線程之間進(jìn)行通信,則使用同步機(jī)制;如果需要隔離多個(gè)線程之間的共享沖突,可以使用ThreadLocal,這將極大地簡(jiǎn)化你的程序,使程序更加易讀、簡(jiǎn)潔。同步用時(shí)間換取空間,ThreadLocal用空間換取時(shí)間。第九頁(yè),共27頁(yè)。自己實(shí)現(xiàn)一個(gè)ThreadLocalpublicclassMyThreadLocal<T>{privateMap<Thread,T>map=Collections.synchronizedMap(newHashMap<Thread,T>());publicTget(){Threadthread=Thread.currentThread();Tvalue=map.get(thread);if(value==null&&!map.containsKey(thread)){value=initialValue();map.put(thread,value);}returnvalue;}
publicvoidset(Tvalue){map.put(Thread.currentThread(),value);}
publicvoidremove(){map.remove(Thread.currentThread());}protectedTinitialValue(){returnnull;}}問(wèn)題:性能低,內(nèi)存無(wú)法釋放1.MyThreadLocal中的map大小會(huì)隨著線程的增加而增加,當(dāng)并發(fā)存在時(shí),效率會(huì)很低。2.當(dāng)前線程結(jié)束后,實(shí)例依然存在,不會(huì)被GC回收,因?yàn)槠渌€程有可能依然還會(huì)在使用。第十頁(yè),共27頁(yè)。源碼實(shí)現(xiàn)第十一頁(yè),共27頁(yè)。源碼實(shí)現(xiàn)publicTget(){Threadt=Thread.currentThread();ThreadLocalMapmap=getMap(t);if(map!=null){ThreadLocalMap.Entrye=map.getEntry(this);if(e!=null)return(T)e.value;}returnsetInitialValue();}注意這里獲取鍵值對(duì)傳進(jìn)去的是this,而不是當(dāng)前線程t。第十二頁(yè),共27頁(yè)。源碼實(shí)現(xiàn)publicvoidset(Tvalue){Threadt=Thread.currentThread();ThreadLocalMapmap=getMap(t);if(map!=null)map.set(this,value);elsecreateMap(t,value);}第十三頁(yè),共27頁(yè)。源碼實(shí)現(xiàn)publicvoidremove(){ThreadLocalMapm=getMap(Thread.currentThread());if(m!=null)m.remove(this);}第十四頁(yè),共27頁(yè)。staticclassThreadLocalMappublicclassThreadLocal<T>{staticclassThreadLocalMap{staticclassEntryextendsWeakReference<ThreadLocal>{Objectvalue;Entry(ThreadLocalk,Objectv){super(k);value=v;}}privateEntry[]table;}}不在ThreadLocal中,它應(yīng)該在哪兒?publicclassThreadimplementsRunnable{ ThreadLocal.ThreadLocalMapinheritableThreadLocals=null;}ThreadLocalMapgetMap(Threadt){returnt.threadLocals;}
voidcreateMap(Threadt,TfirstValue){t.threadLocals=newThreadLocalMap(this,firstValue);}第十五頁(yè),共27頁(yè)。源碼實(shí)現(xiàn)privateTsetInitialValue(){Tvalue=initialValue();Threadt=Thread.currentThread();ThreadLocalMapmap=getMap(t);if(map!=null)map.set(this,value);elsecreateMap(t,value);returnvalue;}第十六頁(yè),共27頁(yè)。WeakReference在Java里,當(dāng)一個(gè)對(duì)象o被創(chuàng)建時(shí),它被放在Heap里.當(dāng)GC運(yùn)行的時(shí)候,如果發(fā)現(xiàn)沒(méi)有任何引用指向o,o就會(huì)被回收以騰出內(nèi)存空間.或者換句話說(shuō),一個(gè)對(duì)象被回收,必須滿足兩個(gè)條件:1)沒(méi)有任何引用指向它2)GC被運(yùn)行.Objectc=newCar();c=null;第十七頁(yè),共27頁(yè)。WeakReferencepublicstaticvoidmain(String[]args){test();}publicstaticvoidtest(){Studentstu=newStudent("張三",12);WeakReference<Student>wr=newWeakReference<>(stu);inti=1;while(true){if(wr.get()!=null){System.out.println("執(zhí)行第"+i+"次");i++;}else{System.out.println("對(duì)象stu被GC自動(dòng)釋放");System.out.println(wr.get());break;}}}
....執(zhí)行第176342次執(zhí)行第176343次執(zhí)行第176344次執(zhí)行第176345次對(duì)象stu被GC自動(dòng)釋放null第十八頁(yè),共27頁(yè)。WeakReference另一作用:如果你想寫(xiě)一個(gè)Java程序,觀察某對(duì)象什么時(shí)候會(huì)被垃圾收集的執(zhí)行緒清除,你必須要用一個(gè)reference記住此對(duì)象,以便隨時(shí)觀察,但是卻因此造成此對(duì)象的reference數(shù)目一直無(wú)法為零,使得對(duì)象無(wú)法被清除。不過(guò),現(xiàn)在有了WeakReference之后,這就可以迎刃而解了。如果你希望能隨時(shí)取得某對(duì)象的信息,但又不想影響此對(duì)象的垃圾收集,那么你應(yīng)該用WeakReference來(lái)記住此對(duì)象,而不是用一般的reference。通常用于Debug、內(nèi)存監(jiān)視等packagejava.lang.ref;SoftReference軟引用HardReference強(qiáng)引用PhantomReference虛引用第十九頁(yè),共27頁(yè)。ThreadLocal實(shí)現(xiàn)流程ThreadLocal是如何為每個(gè)線程創(chuàng)建變量的副本的:首先,在每個(gè)線程Thread內(nèi)部有一個(gè)ThreadLocal.ThreadLocalMap類型的成員變量threadLocals,這個(gè)threadLocals就是用來(lái)存儲(chǔ)實(shí)際的變量副本的,鍵值為當(dāng)前ThreadLocal變量,value為變量副本(即T類型的變量)。初始時(shí),在Thread里面,threadLocals為空,當(dāng)通過(guò)ThreadLocal變量調(diào)用get()方法或者set()方法,就會(huì)對(duì)Thread類中的threadLocals進(jìn)行初始化,并且以當(dāng)前ThreadLocal變量為鍵值,以ThreadLocal要保存的副本變量為value,存到threadLocals。然后在當(dāng)前線程里面,如果要使用副本變量,就可以通過(guò)get方法在threadLocals里面查找。第二十頁(yè),共27頁(yè)。ThreadLoal內(nèi)存模型圖如上圖,ThreadLocalMap使用ThreadLocal的弱引用作為key,如果一個(gè)ThreadLocal沒(méi)有外部強(qiáng)引用引用他,那么系統(tǒng)GC的時(shí)候,這個(gè)ThreadLocal勢(shì)必會(huì)被回收,這樣一來(lái),ThreadLocalMap中就會(huì)出現(xiàn)key為null的Entry,就沒(méi)有辦法訪問(wèn)這些key為null的Entry的value,如果當(dāng)前線程再遲遲不結(jié)束的話,這些key為null的Entry的value就會(huì)一直存在一條強(qiáng)引用鏈:ThreadLocalRef->Thread->ThreaLocalMap->Entry->value永遠(yuǎn)無(wú)法回收,造成內(nèi)存泄露。第二十一頁(yè),共27頁(yè)。getEntryprivateEntrygetEntry(ThreadLocalkey){inti=key.threadLocalHashCode&(table.length-1);Entrye=table[i];if(e!=null&&e.get()==key)returne;elsereturngetEntryAfterMiss(key,i,e);}整理一下ThreadLocalMap的getEntry函數(shù)的流程:首先從ThreadLocal的直接索引位置(通過(guò)ThreadLocal.threadLocalHashCode&(len-1)運(yùn)算得到)獲取Entrye,如果e不為null并且key相同則返回e;如果e為null或者key不一致則向下一個(gè)位置查詢,如果下一個(gè)位置的key和當(dāng)前需要查詢的key相等,則返回對(duì)應(yīng)的Entry,否則,如果key值為null,則擦除該位置的Entry,否則繼續(xù)向下一個(gè)位置查詢?cè)谶@個(gè)過(guò)程中遇到的key為null的Entry都會(huì)被擦除,那么Entry內(nèi)的value也就沒(méi)有強(qiáng)引用鏈,自然會(huì)被回收。仔細(xì)研究代碼可以發(fā)現(xiàn),set操作也有類似的思想,將key為null的這些Entry都刪除,防止內(nèi)存泄露。但是光這樣還是不夠的,上面的設(shè)計(jì)思路依賴一個(gè)前提條件:要調(diào)用ThreadLocalMap的genEntry函數(shù)或者set函數(shù)。這當(dāng)然是不可能任何情況都成立的,所以很多情況下需要使用者手動(dòng)調(diào)用ThreadLocal的remove函數(shù),手動(dòng)刪除不再需要的ThreadLocal,防止內(nèi)存泄露。所以JDK建議將ThreadLocal變量定義成privatestatic的,這樣的話ThreadLocal的生命周期就更長(zhǎng),由于一直存在ThreadLocal的強(qiáng)引用,所以ThreadLocal也就不會(huì)被回收,也就能保證任何時(shí)候都能根據(jù)ThreadLocal的弱引用訪問(wèn)到Entry的value值,然后remove它,防止內(nèi)存泄露。第二十二頁(yè),共27頁(yè)??偨Y(jié)在普通的同步機(jī)制中,是通過(guò)對(duì)象加鎖來(lái)實(shí)現(xiàn)多個(gè)線程對(duì)統(tǒng)一變量的安全訪問(wèn)的,這時(shí)該變量是多個(gè)線程共享的,使用這種同步機(jī)制需要很細(xì)致的分析在什么時(shí)候?qū)ψ兞窟M(jìn)行讀寫(xiě)、什么時(shí)候需要鎖定某個(gè)對(duì)象,什么時(shí)候釋放該對(duì)象的鎖等等。同步機(jī)制中一般使用synchronized關(guān)鍵字來(lái)保證同一時(shí)刻只有一個(gè)線程對(duì)共享變量進(jìn)行操作。但在有些情況下,synchronized不能保證多線程對(duì)共享變量的正確讀寫(xiě)。例如類有一個(gè)類變量,該類變量會(huì)被多個(gè)類方法讀寫(xiě),當(dāng)多線程操作該類的實(shí)例對(duì)象時(shí),如果線程對(duì)類變量有讀取、寫(xiě)入操作就會(huì)發(fā)生類變量讀寫(xiě)錯(cuò)誤,即便是在類方法前加上synchronized也無(wú)效,因?yàn)橥粋€(gè)線程在兩次調(diào)用方法之間時(shí)鎖是被釋放的,這時(shí)其它線程可以訪問(wèn)對(duì)象的類方法,讀取或修改類變量。這種情況下可以將類變量放到ThreadLocal類型的對(duì)象中,使變量在每個(gè)線程中都有獨(dú)立拷貝,不會(huì)出現(xiàn)一個(gè)線程讀取變量時(shí)而被另一個(gè)線程修改的現(xiàn)象。第二十三
溫馨提示
- 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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 企業(yè)防中暑應(yīng)急預(yù)案(10篇)
- 幼兒演講稿錦集10篇
- 企業(yè)財(cái)務(wù)總監(jiān)工作總結(jié)
- DB12T 598.7-2015 天津市建設(shè)項(xiàng)目用地控制指標(biāo) 第7部分:公益性科研機(jī)構(gòu)項(xiàng)目
- 感恩母親演講稿集合五篇
- 學(xué)生的實(shí)習(xí)報(bào)告三篇
- 高等數(shù)學(xué)教程 上冊(cè) 第4版 習(xí)題及答案 P102 第4章 導(dǎo)數(shù)的應(yīng)用
- 影響華法林抗凝效果的藥物
- 舞蹈內(nèi)容課件教學(xué)課件
- 部編版歷史九年級(jí)上冊(cè)第一單元 第2課《古代兩河流域》說(shuō)課稿
- 棒球比賽記錄基礎(chǔ)手冊(cè)
- 跨越門(mén)檻童心出發(fā)-少先隊(duì)儀式教育的成長(zhǎng)探索之路 論文
- 數(shù)字媒體的傳播者和受眾
- cad及天正快捷鍵大全
- 森林防火通道規(guī)范
- GB/T 2910.1-2009紡織品定量化學(xué)分析第1部分:試驗(yàn)通則
- GB/T 28653-2012工業(yè)氟化銨
- GB/T 27021.3-2021合格評(píng)定管理體系審核認(rèn)證機(jī)構(gòu)要求第3部分:質(zhì)量管理體系審核與認(rèn)證能力要求
- GB/T 13914-2013沖壓件尺寸公差
- 井底的四只小青蛙
- FZ/T 52021-2012牛奶蛋白改性聚丙烯腈短纖維
評(píng)論
0/150
提交評(píng)論