同步容器類
常見同步類
Vector
Hashtable
-
Collections.synchronizedXxx工廠方法創(chuàng)建的封裝容器
注:Vector和Hashtable是早期JDK的部分在塔,Collections.synchronizedXxx是JDK1.2添加的
同步容器實現(xiàn)線程安全的方式
- 使用自身的鎖來保護它的每個方法
- 將他們的狀態(tài)封裝起來,并對每個公有方法都進行同步湃窍,使得每次只有一個線程能訪問容器的狀態(tài)
同步容器類的問題
容器上的復合操作存在線程安全問題
-
常見的復合操作:
- 迭代(反復訪問元素,直到遍歷完容器內(nèi)的所有元素)
- 跳轉(zhuǎn)(根據(jù)指定順序找到當前元素的下一個元素)
- 條件運算(例如:若沒有則添加)
//查詢vector.size()狀態(tài)可能是失效的 for(int i=0; i<vector.size(); i++) { doSomething(vector.get(8)); }
``` /** * 先檢查后查詢的復合操作 */ public static Object getLast(Vector list) { //查詢list.size()的狀態(tài)可能是失效的 int lastIndex = list.size() - 1; return list.get(lastIndex); } ```
解決復合操作線程不安全的方法
- 客戶端在復合操作上加鎖(同步容器使用自身的鎖來保護它的每個方法)
sychronized(vector) {
for(int i=0; i<vector.size(); i++) {
doSomething(vector.get(8));
}
}
public static Object getLast(Vector list) {
synchronized(list) {
int lastIndex = list.size() - 1;
return list.get(lastIndex);
}
}
迭代器與ConcurrentModificationException
-
容器迭代的方式
- 直接使用Iterator
- 使用Java5.0引入的for-each
-
迭代器的問題
- 在同步容器的迭代器是并沒有考慮到并發(fā)修改的問題,并且他們表現(xiàn)出的行為是“及時失敗(fail-fast)”漫拭,這意味著碳锈,當他們發(fā)現(xiàn)容器在迭代過程中被修改時,就會拋出一個ConcurrentModificationException異常
- 這種“及時失敗”的迭代器并不是一種完備的處理機制召夹,而只是“善意地”捕獲并發(fā)錯誤岩喷,因此只能作為并發(fā)問題的預警指示
-
解決ConcurrentModificationException問題
-
“克隆”容器,并在副本上進行迭代
注:克隆過程中仍然需要對容器加鎖监憎;克隆容器時存在顯著的性能開銷纱意;這種方式的好壞取決于多個因素,包括容器大小鲸阔,在每個元素上執(zhí)行的工作偷霉,迭代操作相對于容器其他操作的調(diào)用頻率迄委,以及在響應時間和吞吐量等方面的需求
-
-
常見隱藏的迭代
容器的hashCode和equals方法會間接執(zhí)行迭代操作,當容器作為另一個容器的元素或鍵時就會出現(xiàn)這種情況
containsAll类少、removeAll和retainAll等方法
-
將容器作為參數(shù)的構(gòu)造函數(shù)
注:所有間接的迭代操作都可能拋出ConcurrentModificationException
并發(fā)容器
ConcurrentHashMap與Hashtable的區(qū)別
- 相同點:都是線程安全的容器
- 不同點
- 同步方式不同:前者分段同步且讀取操作不同步叙身,并發(fā)性能高;后者全表同步硫狞,并發(fā)性能低
- 內(nèi)存消耗:前者占用內(nèi)存大信轿,因為分段,內(nèi)部容器較多或占用更多額外內(nèi)存
- 迭代器一致性:前者迭代器具有弱一致性妓忍,因為讀取非同步虏两,所以讀取的結(jié)果只包含創(chuàng)建迭代器時已有的元素;后者因為是全表同步世剖,所以保證了迭代器的強一致性
- 增加API:前者增加了一些對常見復合操作的支持定罢,例如“若沒有則添加”、替換以及有條件刪除
- 弱化API:前者弱化了size()旁瘫、isEmpty()方法的語義以反映容器的并發(fā)性祖凫,這些方法的結(jié)果可能是失效的
- 迭代異常:迭代時不會出現(xiàn)ConcurrentModificationException,后者會出現(xiàn)異常
- 并發(fā)容器替代同步容器
- ConcurrentHashMap <- HashTable酬凳、Collections.synchronizedMap(Map)
- ConcurrentSkipListMap <- Collections.synchronizedMap(TreeMap)
- ConcurrentSkipListSet <- Collections.synchronizedSet(TreeSet)
CopyOnWriteArrayList特點
- 寫操作復制內(nèi)部容器惠况,讀操作讀取的是內(nèi)部容器副本,迭代時不會出現(xiàn)ConcurrentModificationException
- 適合于讀操作多宁仔,寫操作少的多線程環(huán)境
中斷
概念
- 中斷是一種用于取消線程操作的協(xié)作機制稠屠;一個線程不能強制其他線程停止正在執(zhí)行的操作而去執(zhí)行其他的操作,但是要求(通知)其他線程執(zhí)行到某個可以暫停的地方停止正在執(zhí)行的操作翎苫,前提是別的線程愿意停止當前的操作
中斷方法介紹
-
isInterruputed 對象方法权埠,獲取線程的中斷狀態(tài)
注:線程死了后,該方法始終返回false
-
interrupt 對象方法煎谍,設(shè)置線程的中斷狀態(tài)
注:如果線程阻塞在Object的wait方法或Thread的join攘蔽、sleep方法上,調(diào)用此方法將清除被阻塞線程的中斷狀態(tài)呐粘,并且調(diào)用這些方法的地方將收到一個InterruptException
-
interrupted 靜態(tài)方法满俗,返回當前線程的中斷狀態(tài),并清除中斷狀態(tài)作岖、
注:線程死了后唆垃,該方法始終返回false
中斷異常InterruptedException處理
- 傳遞InterruptException:不捕獲該異常,或者捕獲該異常然后在執(zhí)行某種簡單的清理工作后再次拋出這個異常
- 恢復中斷:有時候不能拋出InterruptException鳍咱,例如當代碼是Runnable的一部分時降盹,這種情況下必須捕獲InterruptException,并通過調(diào)用當前線程的interrupt方法恢復中斷狀態(tài)谤辜,將中斷信息告知調(diào)用棧中更高層的代碼
同步工具類
概念
- 同步工具類可以是任何對象蓄坏,只要他根據(jù)其自身狀態(tài)來協(xié)調(diào)線程的控制流;例如阻塞隊列可以作為同步工具類
常用同步工具類
- CountDownLatch(閉鎖)
- 延遲線程的進度丑念,直到其達到終止狀態(tài)涡戳,所有線程將釋放進度,當其到達結(jié)束狀態(tài)后脯倚,將不會再改變狀態(tài)
- FutureTask(也可以當做閉鎖)
- 可以返回計算結(jié)果的任務(wù)
- 包括三種狀態(tài):等待運行(Waiting to run)渔彰、正在運行(Running)、運行完成(Completed)
- 執(zhí)行完成表示計算的所有結(jié)束方式:正常結(jié)束推正、由與取消結(jié)束恍涂、由與異常結(jié)束;任務(wù)進入完成狀態(tài)后將會停止在這個狀態(tài)上
- FutureTask表示的計算通過Callable接口來實現(xiàn)
- Callable可以拋出受檢查的或未受檢查的異常植榕,并且任何代碼都可能拋出Error
- 無論任務(wù)代碼拋出什么異常再沧,都會被封裝到一個ExecutionException中,并在Future.get中被重新拋出
- 上面兩條將使get代碼變得復雜尊残,因為不僅需要處理可能出現(xiàn)的ExecutionException以及未受檢查的CancellationException炒瘸,而且還由于ExecutionException是做為一個Throwable類返回的
- 優(yōu)點:提前啟動計算,可以減少等待結(jié)果需要的時間
- Semaphore
- 對資源施加邊界
- CyclicBarrier
- 與閉鎖相同點:柵欄類似于閉鎖寝衫,他能阻塞一組線程直到某個事件發(fā)生
- 與閉鎖不同點:閉鎖是一次性的顷扩,柵欄可多次使用;閉鎖用于等待事件慰毅,而柵欄用于等待其他線程
- Exchanger
- 它是一種Two-Party柵欄隘截,各方在柵欄位置上交換數(shù)據(jù)