




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、The spring of multi-thread The spring of multi-thread 線程安線程安全的另一種解決思路全的另一種解決思路我需要一個可靠的單例模式!上堂回顧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);關于關于VolatileVolatile與同步操作共同使用的思考與同步操作共同使用的思考public class ThreadClient extends Thread private static int count; public void run() for(int i=0;i3;i+) System.out.println(當前線程名:+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(); 當前線程名:當前線程名:Thread-1 count=0當前線程名:當前線程名:Thread-1 count=1當前線程名:當前線程名:Thr
4、ead-1 count=2當前線程名:當前線程名:Thread-2 count=3當前線程名:當前線程名:Thread-2 count=4當前線程名:當前線程名:Thread-2 count=5當前線程名:當前線程名:Thread-0 count=6當前線程名:當前線程名:Thread-0 count=7當前線程名:當前線程名: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(當前線程名:+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)當前線程名:Thread-1 count=null當前線程名:Thread-0 count=nullException in thread Thread-2 java.lang.NullPointerExceptionat com.fanqie.oms.thread.ThreadLocal.ThreadClient.run(ThreadClient.java
8、:21)當前線程名: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(當前線程名:+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(); 當前線
10、程名:Thread-0 count=0當前線程名:Thread-1 count=0當前線程名:Thread-2 count=0當前線程名:Thread-0 count=1當前線程名:Thread-2 count=1當前線程名:Thread-1 count=1當前線程名:Thread-0 count=2當前線程名:Thread-2 count=2當前線程名: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是什么 當使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以
12、獨立地改變自己的副本,而不會影響其它線程所對應的副本。 對外提供的方法很簡單 public T get() ; public void set(T value); public void remove();有利于jvm回收 protected T initialValue(); 返回null ThreadLocal并不能替代同步機制,兩者面向的并不能替代同步機制,兩者面向的問題領域不同。同步機制是為了同步多個線程對相同問題領域不同。同步機制是為了同步多個線程對相同資源的并發訪問,是為了多個線程之間進行通信的有資源的并發訪問,是為了多個線程之間進行通信的有效方式;而效方式;而ThreadLoca
13、l是隔離多個線程的數據共享,是隔離多個線程的數據共享,從根本上就不在多個線程之間共享資源(變量),這從根本上就不在多個線程之間共享資源(變量),這樣當然不需要對多個線程進行同步了。所以,如果你樣當然不需要對多個線程進行同步了。所以,如果你需要進行多個線程之間進行通信,則使用同步機制;需要進行多個線程之間進行通信,則使用同步機制;如果需要隔離多個線程之間的共享沖突,可以使用如果需要隔離多個線程之間的共享沖突,可以使用ThreadLocal,這將極大地簡化你的程序,使程序更,這將極大地簡化你的程序,使程序更加易讀、簡潔。加易讀、簡潔。同步用時間換取空間,ThreadLocal用空間換取時間。自己實
14、現一個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; 問題:性能低,內存無法釋放1.MyThreadLocal中的map大小會隨著線程的增加而增加,當并發存在時,效率會很低。2.當前線程結束后,實例依然存在,不會被GC回收,因為其他線程有可能依然還會在使用。源碼實現源碼實現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(); 注意這里獲取鍵值對傳進去的是 this,而不是當前線程t。源碼實現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); 源碼實現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中,它應該在哪兒?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); 源碼實現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里, 當一個對象o被創建時, 它被放在Heap里. 當GC運行的時候, 如果發現
20、沒有任何引用指向o, o就會被回收以騰出內存空間. 或者換句話說, 一個對象被回收, 必須滿足兩個條件: 1)沒有任何引用指向它 2)GC被運行.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(執行第 + i + 次); i+; else System.out.println(對象stu被GC自動釋放); System.out.println(wr.get(); break; .執行第176342次執行第176343次執行第176344次執行第176345次對象stu被GC自動釋放nullWeakReference 另一作用:如果你想寫一個 Java 程序,觀察某對象什么時候會被垃圾收集的執行緒清除,你必須要用一個 reference 記住此對象,以便隨時觀察,但是卻因此造成此對象的 reference 數目一直無法為零, 使
22、得對象無法被清除。 不過,現在有了 Weak Reference 之后,這就可以迎刃而解了。如果你希望能隨時取得某對象的信息,但又不想影響此對象的垃圾收集,那么你應該用 Weak Reference 來記住此對象,而不是用一般的 reference。通常用于Debug、內存監視等 package java.lang.ref; SoftReference 軟引用 HardReference 強引用 PhantomReference 虛引用ThreadLocal實現流程ThreadLocal是如何為每個線程創建變量的副本的:首先,在每個線程Thread內部有一個ThreadLocal.Thread
23、LocalMap類型的成員變量threadLocals,這個threadLocals就是用來存儲實際的變量副本的,鍵值為當前ThreadLocal變量,value為變量副本(即T類型的變量)。初始時,在Thread里面,threadLocals為空,當通過ThreadLocal變量調用get()方法或者set()方法,就會對Thread類中的threadLocals進行初始化,并且以當前ThreadLocal變量為鍵值,以ThreadLocal要保存的副本變量為value,存到threadLocals。然后在當前線程里面,如果要使用副本變量,就可以通過get方法在threadLocals里面查
24、找。ThreadLoal內存模型圖如上圖,如上圖,ThreadLocalMap使用使用ThreadLocal的弱引用作為的弱引用作為key,如果一個,如果一個ThreadLocal沒有外部強引用引用他,那么系統沒有外部強引用引用他,那么系統GC的時候,這個的時候,這個ThreadLocal勢必會被回收,這樣一來,勢必會被回收,這樣一來,ThreadLocalMap中就會出現中就會出現key為為null的的Entry,就沒有辦法訪問這些就沒有辦法訪問這些key為為null的的Entry的的value,如果當前線程再遲遲不結束,如果當前線程再遲遲不結束的話,這些的話,這些key為為null的的En
25、try的的value就會一直存在一條強引用鏈:就會一直存在一條強引用鏈:ThreadLocal Ref - Thread - ThreaLocalMap - Entry - value永遠無法回收,造成內存泄露。永遠無法回收,造成內存泄露。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函數的流程:首先從ThreadLocal的直接索引位置(通過ThreadLocal.threadLocalHashCode & (len-1)運算得到)獲取Entry e,如果e不為null并且key相同則返回e;如果e為null或者key不一致則向下一個位置查詢,如果下一個位置的key和當前需要查詢的key相等,則返回對應的Entry,否則,如果key值為null,則擦除該位置的Entry,否則繼續向下一個位置查詢在這個過程中遇到的key為null的Entry都會被擦除
27、,那么Entry內的value也就沒有強引用鏈,自然會被回收。仔細研究代碼可以發現,set操作也有類似的思想,將key為null的這些Entry都刪除,防止內存泄露。但是光這樣還是不夠的,上面的設計思路依賴一個前提條件:要調用ThreadLocalMap的genEntry函數或者set函數。這當然是不可能任何情況都成立的,所以很多情況下需要使用者手動調用ThreadLocal的remove函數,手動刪除不再需要的ThreadLocal,防止內存泄露。所以JDK建議將ThreadLocal變量定義成private static的,這樣的話ThreadLocal的生命周期就更長,由于一直存在Thr
28、eadLocal的強引用,所以ThreadLocal也就不會被回收,也就能保證任何時候都能根據ThreadLocal的弱引用訪問到Entry的value值,然后remove它,防止內存泄露。總結 在普通的同步機制中,是通過對象加鎖來實現多個線程對統一變量的安全訪問的,這時該變量是多個線程共享的,使用這種同步機制需要很細致的分析在什么時候對變量進行讀寫、什么時候需要鎖定某個對象,什么時候釋放該對象的鎖等等。同步機制中一般使用synchronized關鍵字來保證同一時刻只有一個線程對共享變量進行操作。但在有些情況下,synchronized不能保證多線程對共享變量的正確讀寫。例如類有一個類變量,該
29、類變量會被多個類方法讀寫,當多線程操作該類的實例對象時,如果線程對類變量有讀取、寫入操作就會發生類變量讀寫錯誤,即便是在類方法前加上synchronized也無效,因為同一個線程在兩次調用方法之間時鎖是被釋放的,這時其它線程可以訪問對象的類方法,讀取或修改類變量。 這種情況下可以將類變量放到ThreadLocal類型的對象中,使變量在每個線程中都有獨立拷貝,不會出現一個線程讀取變量時而被另一個線程修改的現象。總結 同步機制是為了同步多個線程對相同資源的并發訪問,是為了多個線程之間進行通信的有效方式; 而threadLocal是隔離多個線程的數據共享,從根本上就不在多個線程之間共享變量,這樣當然
30、不需要對多個線程進行同步了。 ThreadLocal是解決線程安全問題一個很好的思路,它通過為每個線程提供一個獨立的變量副本解決了變量并發訪問的沖突問題。在很多情況下,ThreadLocal比直接使用synchronized同步機制解決線程安全問題更簡單,更方便,且結果程序擁有更高的并發性。使用場景我們做web開發時的 web層的Action-業務邏輯層的Service-數據訪問層的DAO,當我們要在這三層中共享參數時,那么我們就可以使用ThreadLocal 了。 1.在某個接口中定義一個靜態的ThreadLocal 對象, 例如 public static ThreadLocal threadLocal=new ThreadL
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 林業扶持資金管理辦法
- 電廠運行管理標準化操作指南與實踐探索
- 誘變劑增強納米載體穿透-洞察及研究
- 小學班級德育課程化實踐研究
- 檢測公司績效管理辦法
- 園林綠化人員崗位職責
- 智能電網數據傳輸優化:面向未來的設計方案
- 安全生產月活動情況匯報
- 名家語文教學示范
- 根據安全法的規定
- 明渠均勻流計算公式
- 林規發防護林造林工程投資估算指標
- 四年級上冊 口算題 1000題
- 九上道法知識點梳理(全冊)-九年級道德與法治上冊必備知識梳理總結(部編版)
- YB/T 5202.1-2003不定形耐火材料試樣制備方法第1部分:耐火澆注料
- GB/T 700-2006碳素結構鋼
- GB/T 41419-2022數字化試衣虛擬人體用術語和定義
- GB/T 24218.1-2009紡織品非織造布試驗方法第1部分:單位面積質量的測定
- 《病毒學》(研究生)全冊配套完整課件
- 第十七章其他熔化焊接與熱切割作業課件
- 腧穴總論 2特定穴課件
評論
0/150
提交評論