![Thespringofmulti-thread——線程安全的另一種解決思路_第1頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/2/61a418e4-50bf-40b6-bcbb-96abf0782360/61a418e4-50bf-40b6-bcbb-96abf07823601.gif)
![Thespringofmulti-thread——線程安全的另一種解決思路_第2頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/2/61a418e4-50bf-40b6-bcbb-96abf0782360/61a418e4-50bf-40b6-bcbb-96abf07823602.gif)
![Thespringofmulti-thread——線程安全的另一種解決思路_第3頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/2/61a418e4-50bf-40b6-bcbb-96abf0782360/61a418e4-50bf-40b6-bcbb-96abf07823603.gif)
![Thespringofmulti-thread——線程安全的另一種解決思路_第4頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/2/61a418e4-50bf-40b6-bcbb-96abf0782360/61a418e4-50bf-40b6-bcbb-96abf07823604.gif)
![Thespringofmulti-thread——線程安全的另一種解決思路_第5頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/2/61a418e4-50bf-40b6-bcbb-96abf0782360/61a418e4-50bf-40b6-bcbb-96abf07823605.gif)
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、The spring of multi-thread The spring of multi-thread 線程安線程安全的另一種解決思路全的另一種解決思路我需要一個(gè)可靠的單例模式!上堂回顧public class InnDao private volatile static InnDao instance;private InnDao()public static InnDao getInstance() if (instance = null)synchronized (InnDao.class) / 1if (instance = null) / 2 instance = new Inn
2、Dao(); / 3return instance;public final Model.Finder finder = new Model.Finder(Integer.class, Inn.class);關(guān)于關(guān)于VolatileVolatile與同步操作共同使用的思考與同步操作共同使用的思考public class ThreadClient extends Thread private static int count; public void run() for(int i=0;i3;i+) System.out.println(當(dāng)前線程名:+Thread.currentThread()
3、.getName() + count=+count); count+; public static void main(String args) ThreadClient tc1 = new ThreadClient(); ThreadClient tc2 = new ThreadClient(); ThreadClient tc3 = new ThreadClient(); tc1.start(); tc2.start(); tc3.start(); 當(dāng)前線程名:當(dāng)前線程名:Thread-1 count=0當(dāng)前線程名:當(dāng)前線程名:Thread-1 count=1當(dāng)前線程名:當(dāng)前線程名:Thr
4、ead-1 count=2當(dāng)前線程名:當(dāng)前線程名:Thread-2 count=3當(dāng)前線程名:當(dāng)前線程名:Thread-2 count=4當(dāng)前線程名:當(dāng)前線程名:Thread-2 count=5當(dāng)前線程名:當(dāng)前線程名:Thread-0 count=6當(dāng)前線程名:當(dāng)前線程名:Thread-0 count=7當(dāng)前線程名:當(dāng)前線程名:Thread-0 count=8public class ThreadClient extends Thread private static ThreadLocal count = new ThreadLocal(); public ThreadClient(Inte
5、ger value) count.set(value); public void run() for(int i=0;i3;i+) System.out.println(當(dāng)前線程名:+Thread.currentThread().getName() + count=+count.get(); count.set(count.get()+1); try TimeUnit.SECONDS.sleep(1); catch (InterruptedException e) e.printStackTrace(); public static void main(String args) ThreadC
6、lient tc1 = new ThreadClient(0); ThreadClient tc2 = new ThreadClient(0); ThreadClient tc3 = new ThreadClient(0); tc1.start(); tc2.start(); tc3.start(); Exception in thread Thread-1 java.lang.NullPointerExceptionat com.fanqie.oms.thread.ThreadLocal.ThreadClient.run(ThreadClient.java:21)Exception in t
7、hread Thread-0 java.lang.NullPointerExceptionat com.fanqie.oms.thread.ThreadLocal.ThreadClient.run(ThreadClient.java:21)當(dāng)前線程名:Thread-1 count=null當(dāng)前線程名:Thread-0 count=nullException in thread Thread-2 java.lang.NullPointerExceptionat com.fanqie.oms.thread.ThreadLocal.ThreadClient.run(ThreadClient.java
8、:21)當(dāng)前線程名:Thread-2 count=nullpublic class ThreadClient extends Thread private static ThreadLocal count = new ThreadLocal() Override protected Integer initialValue() return 0; ; public void run() for(int i=0;i3;i+) System.out.println(當(dāng)前線程名:+Thread.currentThread().getName() + count=+count.get(); count
9、.set(count.get()+1); try TimeUnit.SECONDS.sleep(1); catch (InterruptedException e) e.printStackTrace(); public static void main(String args) ThreadClient tc1 = new ThreadClient(); ThreadClient tc2 = new ThreadClient(); ThreadClient tc3 = new ThreadClient(); tc1.start(); tc2.start(); tc3.start(); 當(dāng)前線
10、程名:Thread-0 count=0當(dāng)前線程名:Thread-1 count=0當(dāng)前線程名:Thread-2 count=0當(dāng)前線程名:Thread-0 count=1當(dāng)前線程名:Thread-2 count=1當(dāng)前線程名:Thread-1 count=1當(dāng)前線程名:Thread-0 count=2當(dāng)前線程名:Thread-2 count=2當(dāng)前線程名:Thread-1 count=2TimeUnitpublic void test(String args) throws InterruptedException System.out.println(Sleeping for 4 minut
11、es using Thread.sleep(); Thread.sleep(4 * 60 * 1000); System.out.println(Sleeping for 4 minutes using TimeUnit sleep(); TimeUnit.SECONDS.sleep(4); TimeUnit.MINUTES.sleep(4); TimeUnit.HOURS.sleep(1); TimeUnit.DAYS.sleep(1); ThreadLocal是什么 當(dāng)使用ThreadLocal維護(hù)變量時(shí),ThreadLocal為每個(gè)使用該變量的線程提供獨(dú)立的變量副本,所以每一個(gè)線程都可以
12、獨(dú)立地改變自己的副本,而不會(huì)影響其它線程所對應(yīng)的副本。 對外提供的方法很簡單 public T get() ; public void set(T value); public void remove();有利于jvm回收 protected T initialValue(); 返回null ThreadLocal并不能替代同步機(jī)制,兩者面向的并不能替代同步機(jī)制,兩者面向的問題領(lǐng)域不同。同步機(jī)制是為了同步多個(gè)線程對相同問題領(lǐng)域不同。同步機(jī)制是為了同步多個(gè)線程對相同資源的并發(fā)訪問,是為了多個(gè)線程之間進(jìn)行通信的有資源的并發(fā)訪問,是為了多個(gè)線程之間進(jìn)行通信的有效方式;而效方式;而ThreadLoca
13、l是隔離多個(gè)線程的數(shù)據(jù)共享,是隔離多個(gè)線程的數(shù)據(jù)共享,從根本上就不在多個(gè)線程之間共享資源(變量),這從根本上就不在多個(gè)線程之間共享資源(變量),這樣當(dāng)然不需要對多個(gè)線程進(jìn)行同步了。所以,如果你樣當(dāng)然不需要對多個(gè)線程進(jìn)行同步了。所以,如果你需要進(jìn)行多個(gè)線程之間進(jìn)行通信,則使用同步機(jī)制;需要進(jìn)行多個(gè)線程之間進(jìn)行通信,則使用同步機(jī)制;如果需要隔離多個(gè)線程之間的共享沖突,可以使用如果需要隔離多個(gè)線程之間的共享沖突,可以使用ThreadLocal,這將極大地簡化你的程序,使程序更,這將極大地簡化你的程序,使程序更加易讀、簡潔。加易讀、簡潔。同步用時(shí)間換取空間,ThreadLocal用空間換取時(shí)間。自己實(shí)
14、現(xiàn)一個(gè)ThreadLocalpublic class MyThreadLocal private Map map = Collections.synchronizedMap(new HashMap(); public T get() Thread thread = Thread.currentThread(); T value = map.get(thread); if(value = null & !map.containsKey(thread) value = initialValue(); map.put(thread,value); return value; public v
15、oid set(T value) map.put(Thread.currentThread(),value); public void remove() map.remove(Thread.currentThread(); protected T initialValue() return null; 問題:性能低,內(nèi)存無法釋放1.MyThreadLocal中的map大小會(huì)隨著線程的增加而增加,當(dāng)并發(fā)存在時(shí),效率會(huì)很低。2.當(dāng)前線程結(jié)束后,實(shí)例依然存在,不會(huì)被GC回收,因?yàn)槠渌€程有可能依然還會(huì)在使用。源碼實(shí)現(xiàn)源碼實(shí)現(xiàn)public T get() Thread t = Thread.curre
16、ntThread(); ThreadLocalMap map = getMap(t); if (map != null) ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; return setInitialValue(); 注意這里獲取鍵值對傳進(jìn)去的是 this,而不是當(dāng)前線程t。源碼實(shí)現(xiàn)public void set(T value) Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map !
17、= null) map.set(this, value); else createMap(t, value); 源碼實(shí)現(xiàn)public void remove() ThreadLocalMap m = getMap(Thread.currentThread(); if (m != null) m.remove(this); static class ThreadLocalMappublic class ThreadLocal static class ThreadLocalMapstatic class Entry extends WeakReference Object value; Entr
18、y(ThreadLocal k, Object v) super(k); value = v; private Entry table; 不在ThreadLocal中,它應(yīng)該在哪兒?public class Thread implements Runnable ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;ThreadLocalMap getMap(Thread t) return t.threadLocals; void createMap(Thread t, T firstValue) t.threadLocals =
19、new ThreadLocalMap(this, firstValue); 源碼實(shí)現(xiàn)private T setInitialValue() T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; WeakReference在Java里, 當(dāng)一個(gè)對象o被創(chuàng)建時(shí), 它被放在Heap里. 當(dāng)GC運(yùn)行的時(shí)候, 如果發(fā)現(xiàn)
20、沒有任何引用指向o, o就會(huì)被回收以騰出內(nèi)存空間. 或者換句話說, 一個(gè)對象被回收, 必須滿足兩個(gè)條件: 1)沒有任何引用指向它 2)GC被運(yùn)行.Object c = new Car();c = null;WeakReferencepublic static void main(String args) test();public static void test() Student stu = new Student(張三, 12); WeakReference wr = new WeakReference(stu); int i = 1; while (true) if (wr.get()
21、 != null) System.out.println(執(zhí)行第 + i + 次); i+; else System.out.println(對象stu被GC自動(dòng)釋放); System.out.println(wr.get(); break; .執(zhí)行第176342次執(zhí)行第176343次執(zhí)行第176344次執(zhí)行第176345次對象stu被GC自動(dòng)釋放nullWeakReference 另一作用:如果你想寫一個(gè) Java 程序,觀察某對象什么時(shí)候會(huì)被垃圾收集的執(zhí)行緒清除,你必須要用一個(gè) reference 記住此對象,以便隨時(shí)觀察,但是卻因此造成此對象的 reference 數(shù)目一直無法為零, 使
22、得對象無法被清除。 不過,現(xiàn)在有了 Weak Reference 之后,這就可以迎刃而解了。如果你希望能隨時(shí)取得某對象的信息,但又不想影響此對象的垃圾收集,那么你應(yīng)該用 Weak Reference 來記住此對象,而不是用一般的 reference。通常用于Debug、內(nèi)存監(jiān)視等 package java.lang.ref; SoftReference 軟引用 HardReference 強(qiáng)引用 PhantomReference 虛引用ThreadLocal實(shí)現(xiàn)流程ThreadLocal是如何為每個(gè)線程創(chuàng)建變量的副本的:首先,在每個(gè)線程Thread內(nèi)部有一個(gè)ThreadLocal.Thread
23、LocalMap類型的成員變量threadLocals,這個(gè)threadLocals就是用來存儲(chǔ)實(shí)際的變量副本的,鍵值為當(dāng)前ThreadLocal變量,value為變量副本(即T類型的變量)。初始時(shí),在Thread里面,threadLocals為空,當(dāng)通過ThreadLocal變量調(diào)用get()方法或者set()方法,就會(huì)對Thread類中的threadLocals進(jìn)行初始化,并且以當(dāng)前ThreadLocal變量為鍵值,以ThreadLocal要保存的副本變量為value,存到threadLocals。然后在當(dāng)前線程里面,如果要使用副本變量,就可以通過get方法在threadLocals里面查
24、找。ThreadLoal內(nèi)存模型圖如上圖,如上圖,ThreadLocalMap使用使用ThreadLocal的弱引用作為的弱引用作為key,如果一個(gè),如果一個(gè)ThreadLocal沒有外部強(qiáng)引用引用他,那么系統(tǒng)沒有外部強(qiáng)引用引用他,那么系統(tǒng)GC的時(shí)候,這個(gè)的時(shí)候,這個(gè)ThreadLocal勢必會(huì)被回收,這樣一來,勢必會(huì)被回收,這樣一來,ThreadLocalMap中就會(huì)出現(xiàn)中就會(huì)出現(xiàn)key為為null的的Entry,就沒有辦法訪問這些就沒有辦法訪問這些key為為null的的Entry的的value,如果當(dāng)前線程再遲遲不結(jié)束,如果當(dāng)前線程再遲遲不結(jié)束的話,這些的話,這些key為為null的的En
25、try的的value就會(huì)一直存在一條強(qiáng)引用鏈:就會(huì)一直存在一條強(qiáng)引用鏈:ThreadLocal Ref - Thread - ThreaLocalMap - Entry - value永遠(yuǎn)無法回收,造成內(nèi)存泄露。永遠(yuǎn)無法回收,造成內(nèi)存泄露。getEntryprivate Entry getEntry(ThreadLocal key) int i = key.threadLocalHashCode & (table.length - 1); Entry e = tablei; if (e != null & e.get() = key) return e; else return
26、 getEntryAfterMiss(key, i, e); 整理一下ThreadLocalMap的getEntry函數(shù)的流程:首先從ThreadLocal的直接索引位置(通過ThreadLocal.threadLocalHashCode & (len-1)運(yùn)算得到)獲取Entry e,如果e不為null并且key相同則返回e;如果e為null或者key不一致則向下一個(gè)位置查詢,如果下一個(gè)位置的key和當(dāng)前需要查詢的key相等,則返回對應(yīng)的Entry,否則,如果key值為null,則擦除該位置的Entry,否則繼續(xù)向下一個(gè)位置查詢在這個(gè)過程中遇到的key為null的Entry都會(huì)被擦除
27、,那么Entry內(nèi)的value也就沒有強(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變量定義成private static的,這樣的話ThreadLocal的生命周期就更長,由于一直存在Thr
28、eadLocal的強(qiáng)引用,所以ThreadLocal也就不會(huì)被回收,也就能保證任何時(shí)候都能根據(jù)ThreadLocal的弱引用訪問到Entry的value值,然后remove它,防止內(nèi)存泄露??偨Y(jié) 在普通的同步機(jī)制中,是通過對象加鎖來實(shí)現(xiàn)多個(gè)線程對統(tǒng)一變量的安全訪問的,這時(shí)該變量是多個(gè)線程共享的,使用這種同步機(jī)制需要很細(xì)致的分析在什么時(shí)候?qū)ψ兞窟M(jìn)行讀寫、什么時(shí)候需要鎖定某個(gè)對象,什么時(shí)候釋放該對象的鎖等等。同步機(jī)制中一般使用synchronized關(guān)鍵字來保證同一時(shí)刻只有一個(gè)線程對共享變量進(jìn)行操作。但在有些情況下,synchronized不能保證多線程對共享變量的正確讀寫。例如類有一個(gè)類變量,該
29、類變量會(huì)被多個(gè)類方法讀寫,當(dāng)多線程操作該類的實(shí)例對象時(shí),如果線程對類變量有讀取、寫入操作就會(huì)發(fā)生類變量讀寫錯(cuò)誤,即便是在類方法前加上synchronized也無效,因?yàn)橥粋€(gè)線程在兩次調(diào)用方法之間時(shí)鎖是被釋放的,這時(shí)其它線程可以訪問對象的類方法,讀取或修改類變量。 這種情況下可以將類變量放到ThreadLocal類型的對象中,使變量在每個(gè)線程中都有獨(dú)立拷貝,不會(huì)出現(xiàn)一個(gè)線程讀取變量時(shí)而被另一個(gè)線程修改的現(xiàn)象。總結(jié) 同步機(jī)制是為了同步多個(gè)線程對相同資源的并發(fā)訪問,是為了多個(gè)線程之間進(jìn)行通信的有效方式; 而threadLocal是隔離多個(gè)線程的數(shù)據(jù)共享,從根本上就不在多個(gè)線程之間共享變量,這樣當(dāng)然
30、不需要對多個(gè)線程進(jìn)行同步了。 ThreadLocal是解決線程安全問題一個(gè)很好的思路,它通過為每個(gè)線程提供一個(gè)獨(dú)立的變量副本解決了變量并發(fā)訪問的沖突問題。在很多情況下,ThreadLocal比直接使用synchronized同步機(jī)制解決線程安全問題更簡單,更方便,且結(jié)果程序擁有更高的并發(fā)性。使用場景我們做web開發(fā)時(shí)的 web層的Action-業(yè)務(wù)邏輯層的Service-數(shù)據(jù)訪問層的DAO,當(dāng)我們要在這三層中共享參數(shù)時(shí),那么我們就可以使用ThreadLocal 了。 1.在某個(gè)接口中定義一個(gè)靜態(tài)的ThreadLocal 對象, 例如 public static ThreadLocal threadLocal=new ThreadL
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025-2030年抗氧化維生素E行業(yè)跨境出海戰(zhàn)略研究報(bào)告
- 摩托車駕駛員傷害與事故責(zé)任認(rèn)定考核試卷
- 2025-2030年手工臺(tái)歷設(shè)計(jì)行業(yè)跨境出海戰(zhàn)略研究報(bào)告
- 2025-2030年即食百合蓮子粥企業(yè)制定與實(shí)施新質(zhì)生產(chǎn)力戰(zhàn)略研究報(bào)告
- 明星女藝人網(wǎng)絡(luò)直播合作協(xié)議書(2025版)
- 2025年度生態(tài)旅游區(qū)土地租賃與開發(fā)管理合同
- 建筑裝飾工程智能家居技術(shù)應(yīng)用考核試卷
- 2025年度農(nóng)藥行業(yè)政策研究與解讀合同
- 消防演練的活動(dòng)總結(jié)(集合15篇)
- 生日的講話稿
- 農(nóng)電公司績效考核管理辦法
- 斜拉橋施工技術(shù)之斜拉索圖文并茂
- 心肌梗死的心電圖改變
- 三星SHP-DP728指紋鎖說明書
- 預(yù)應(yīng)力錨索張拉及封錨
- 烤煙生產(chǎn)沿革
- GB 1886.227-2016食品安全國家標(biāo)準(zhǔn)食品添加劑嗎啉脂肪酸鹽果蠟
- 毛澤東思想課件-第七章 毛澤東思想的活的靈魂
- 公共關(guān)系效果的評(píng)估課件
- 建筑施工安全員理論考核試題與答案
- 高速公路用地勘測定界及放線定樁技術(shù)標(biāo)書
評(píng)論
0/150
提交評(píng)論