亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

Java并發編程實踐

系統 1748 0

第一章 ? 介紹

線程的優點
使用多處理器
對異步事件的處理
用戶界面的更加響應性
線程的風險
? 1.安全危險
? 2.活躍度危險
? 3.性能危險
線程無處不在
1.定時器
2.JSP
3.RMI
4.Swing和AWT

?

?

?


第二章 ? 線程安全
編寫線程安全的代碼,本質上就是管理對狀態的訪問,而通常都是共享的、可變的狀態?
無論何時,只要有多余一個的線程訪問給定的狀態變量,而且其中某個線程會寫入該變量,此時必須使用同步來協調線程對該變量的訪問。

在沒有正確同步的情況下,如果多個線程訪問了同一個變量,你的程序就存在隱患,有三種方法修復它:
1.不要跨線程共享變量
2.使狀態變量為不可變的;或者
3.在任何訪問狀態變量的時候使用同步
?
當多個線程訪問一個類時,如果不用考慮這些線程在運行時環境下的調度和交替執行,并且不需要額外的同步及在調用方代碼不必作其他的協調,這個類的行為仍然是正確的,那么稱這個類是線程安全的。

無狀態對象永遠是線程安全的

假設有操作A和B,如果從執行A的線程角度看,當其他線程執行B時,要么B全部執行完成,要么一點都沒有執行,這樣A和B互為原子操作,一個原子操作是指:該操作對于所有的操作,包括它自己,都滿足前面的狀態

利用AtomicLong這樣已有的線程安全對象管理類的狀態是非常實用的,相對于非線程安全對象,判斷一個線程安全對象的可能狀態和狀態轉換要容易的多,這簡化了維護和驗證線程安全性的工作

為了保護狀態的一致性,要在單一的原子操作中更新相互關聯的狀態變量

對于每一個涉及多個變量的不變約束,需要同一個鎖保護其所有的變量

通常簡單性和性能之間是相互牽制的,實現一個同步策略時,不要過早地為了性能而犧牲簡單性(這是對安全性潛在的妥協)

有些耗時的計算或操作,比如網絡或者控制臺IO,難以快速完成,執行這些操作期間不要占有鎖


?

?


第三章 共享對象
在沒有同步的情況下,編譯器,處理器,運行時安排操作的順序可能完全出人意料。在沒有進行適當同步的多線程程序中,嘗試推斷那些“必然”
發生在內存中的動作時,你總是會判斷錯誤。

JDK對于long和double類型來說,如果沒有聲明為volatile的話,則是非原子操作。也就是說對于一個int類型來說,如果沒有賦值成功,那就是默認值,成功就是新的值,但非原子的long和double可能會出現一些中間值。

鎖不僅僅是關于同步互斥的,也是關于內存可見的。為了保證所有線程都能夠看到共享的、可變變量的最新值,讀取和寫入線程必須使用公共的鎖進行同步。

只 有當volatile變量能夠簡化實現和同步策略的驗證時,才使用它們。當驗證正確性必須推斷可見性問題時,應該避免使用volatile變量。正確使用 volatile變量的方式包括:用于確保它們所引起的對象狀態的可見性,或者用于標識重要的生命周期事件(比如初始化或者關閉)的發生。

加鎖可以保證可見性與原子性;volatile變量只能保證可見性。
只有滿足了下面所有的標準后,你才能使用volatile變量:
1.寫入變量時并不依賴變量的當前值;或者能夠確保只有單一的線程修改變量的值;
2.變量不需要與其他的狀態變量共同參與不變約束;
3.而且,訪問變量時,沒有其他原因需要加鎖。

發布一個對象的意思是使它能夠被當前范圍之外的代碼所使用
不要讓this在構造期間逸出,甚至即使再構造函數的最后一行發布的引用也是如此,這樣的對象為稱為“沒有正確的構建”

不可變對象永遠是線程安全的
只有滿足如下狀態,一個對象才是不可變的:
1.它的狀態不能再創建后再被修改
2.所有的域都是final類型;并且
3.它被正確創建(創建期間沒有發生this引用逸出)

正如“將所有的域聲明為私有的,除非它們需要更高的可見性“一樣,將所有的域聲明為final型,除非它們是可變的

不可變對象可以再沒有額外同步的情況下,安全地用于任意線程;甚至發布它們不需要額外的同步

為了安全地發布對象,對象的引用以及對象的狀態必須同時對其他線程可見,一個正確創建的對象可以通過下列條件安全發布:
1.通過靜態初始化器初始化對象的引用;
2.將它們的引用存儲到volatile域或AtomicReference;
3.將它們的引用存儲到正確創建的對象的final域中;
4.或者將它的引用存儲到由鎖正確保護的域中

任何線程都可以再沒有額外的同步下安全地使用一個安全發布的高效不可變對象。

發布對象的必要條件依賴于對象的可變性:
1.不可變對象可以通過任意機制發布
2.高效不可變對象必須要安全發布
3.可變對象必須要安全發布,同時必須要線程安全或者是被鎖保護。

在并發程序中,使用和共享對象的一些最有效的策略如下:
1.線程限制:一個線程限制的對象,通過限制在線程中,而被線程獨占,且只能被占有它的線程修改。
2.共享只讀:一個共享的只讀對象,在沒有額外同步的情況下,可以被多個線程并發地訪問,但是任何線程不能修改它。共享只讀對象包括可變對象與
?? 高效不可變對象
3.共享線程安全:一個線程安全的對象在內部進行同步,所以其他線程無需額外同步,就可以通過公共接口隨意的訪問它。
4.被守護的:一個被守護的對象只能通過特定的鎖來訪問。被守護的對象包括哪些被線程安全對象封裝的對象,和已知被特定的鎖保護起來的已發布對象。

?

?

?


第四章 ? 組合對象
設計線程安全類的過程應該包括下面三個基本要素:
1.確定對象狀態是由哪些變量構成的
2.確定限制狀態變量的不變約束
3.制定一個管理并發訪問對象狀態的策略

不理解對象的不變約束和后驗條件,你就不能保證線程安全性。要約束狀態變量的有效值或者狀態轉換,就需要原子性與封裝性。

將數據封裝在對象內部,把對數據的訪問限制在對象的方法上,更易確保線程在訪問數據時總能獲得正確的鎖

限制性使構造線程安全的類變得更容易。因為類的狀態被限制后,分析它的線程安全性時,就不必檢查完整的程序。
java監視器模式

如果一個累由多個彼此獨立的線程安全的狀態變量組成,并且類的操作不包含任何無效狀態轉換時,可以將線程安全委托給這些狀態變量。

如果一個狀態變量是線程安全的,沒有任何不變約束限制它的值,并且沒有任何狀態轉換限制它的操作,那么它可以被安全發布。

為類的用戶編寫類線程安全性擔保的文檔;為類的維護者編寫類的同步策略文檔。

?

?

?


第五章 ? 構建塊
同步容器,同步容器的組合操作(可能需要外部加鎖)
迭代器和ConcurrentModificationException
并發容器
ConcurrentHashMap,采用分離鎖機制
CopyOnWriteArrayList
CopyOnWriteArraySet

有界隊列是強大的資源管理工具,用來簡歷可靠的應用程序:它們歇制那些可以產生過多工作量、具有威脅的活動,從而讓你的程序在面對超負荷
工作時更加健壯。
雖然生產者-消費者模式可以把生產者和消費者的代碼互相解耦合,但是它們的行為還是間接地通過共享工作隊列耦合在一起了。它理想地假定消費者
會持續工作,所以你不需要為工作隊列的大小劃定邊界,但是這將成為日后需要重新架構系統的預兆。在你的設計初期就是用阻塞隊列建立對資源的
管理--提早做這件事情會比日后再修復容易得多。

雙端隊列和竊取工作
阻塞和可中斷的方法

閉鎖是一種Synchronizer,它可以延遲線程的進度直到線程到達終止狀態。一個閉鎖工作起來就像一道大門:直到閉鎖達到終點狀態之前門一直
是關閉的,沒有線程能夠通過,在終點狀態到來的時候,門開了,允許所有的線程通過。
FutureTask
信號量:用來控制能夠同時訪問某特定資源的活動數量,或者同時執行某一給定操作的數量。
關卡

?

?

?


第一部分? 總結
1.可變狀態,伙計們? 所有并發問題都歸結為如何協調訪問并發狀態??勺儬顟B越少,保證線程安全就越容易。
2.盡量將域聲明為final類型,除非它們的需要是可變的。
3.不可變狀態天生是線程安全的? 不可變對象極大地減輕了并發編程的壓力。它們簡單而安全,可以在沒有鎖或者防御性復制的情況下自由地共享。
4.封裝使管理復雜度變得更可行?? 你固然可以用存儲于全局變量的數據來寫一個線程安全類。但是你為什么要這么做?在對象中封裝數據,讓它們
???? 能夠更加容易地保持不變;在對象中封裝同步,使它們能夠更容易地遵守同步策略。
5.用鎖來守護每一個可變變量
6.對同一不變約束中的所有變量都使用相同的鎖。
7.在運行復合操作期間持有鎖。
8.在非同步的多線程情況下,訪問可變變量的程序是存在隱患的。
9.不要依賴于可以需要同步的小聰明
10.在設計過程中就要考慮線程安全性?;蛘咴谖臋n中明確地說明它不是線程安全的。
11.文檔話你的同步策略。

?

?

?

?

?

第六章 執行任務
圍繞執行任務來管理應用程序時,第一步要指明一個清晰的任務邊界。理想情況下,任務是獨立的活動:它的工作并不依賴于其他任務的狀態、結果
或者邊界效應。
無限創建線程的缺點:
1.線程生命周期的開銷
2.資源消耗量
3.穩定性

Executor框架
無論何時當你看到這種形式的代碼:
new Thread(runnable).start()
并且你可能最終希望獲得一個更加靈活的執行策略時,請認真考慮使用Executor代替Thread。
1.newFixedThreadPool()
2.newCachedThreadPool()
3.newSingleThreadExecutor()
4.newScheduleThreadPool()
Executor的生命周期

可攜帶結果的任務:Callable和Future
當一些線程執行了耗時的任務,需要等待,我們可以將這些任務線程全部放到一個ExecutorService中,當所有的線程都
執行完后,就會返回一個結果集,我們就可以操作這個集了,但是它的響應性不太高。

CompletionService 內部使用了BlockQueue,它可以返回一個Future,這個Future可以一直阻塞等待(獲取CompletionService
中的BlockQueue元素),也可以設置等待超時。
有了CompletionService,若干個耗時線程,只要有一個完成,就可以返回,我們拿到Future就可以執行了。


?

?


第七章 取消和關閉?
當外部代碼能夠在活動自然完成之前,把它更改為完成狀態,那么這個活動被稱為可取消的。
1.用戶請求取消
2.限制活動
3.應用程序事件
4.錯誤
5.關閉

在API和語言規范中,并有把中斷與任何取消的語意綁定起來,但是,實際上使用中斷來處理取消之外的任何事情都是不明智的,并且很難支撐
起更大的應用。

調用interrupt并不意味著必然停止目標線程正在進行的工作;它僅僅是傳遞了請求中斷的消息。

中斷通常是實現取消最明智的選擇。

支持中斷取消的代碼:
public void?run() {
??? try {
????????BingInteger?p =?BigInteger.ONE;
????????while(!Thread.currentThread().isInterrupted()) {
??????????? queue.put(p=p.nextProbalePrime());
??????? }
??? }?catch(InterruptedException?e) {
????????//允許線程退出
??? }
}

因為每一個線程都其自己的中斷策略,所以你不應用中斷線程,除非你想知道中斷對這個線程意味著什么。
有兩種處理InterruptedException的實用策略:
1.傳遞異常(很可能發生在特定任務的清除時),使你的方法也成為可中斷的阻塞方法
2.或者保存中斷狀態,上層調用棧中的代碼能夠對其進行處理

只有實現了線程中斷策略的代碼才可以接受中斷請求。通用目的的任務和庫的代碼決不應該接受中斷請求。

當Feture.get()拋出InterruptedException或TimeoutException時,如果你知道不再需要結果時,就可以調用Future.cancel()來取消任務了。

處理不可中斷的阻塞:
1.java.io中的同步Socket I/O
2.java.nio中的同步I/O
3.Selector的異步I/O
4.獲得鎖

對于線程的持有的服務,只要服務的存在時間大于創建線程的方法存在的時間,那么久應該提供聲明周期方法。
不支持關閉的線程是不太好的,因為可能會錯過一些日志
可以通過線程池來管理關閉相關的服務
增加致命藥丸關閉服務

在一個長時間運行的應用程序中,所有的線程都要給未捕獲異常設置一個處理器,這個處理器至少要將異常信息記入日志中。
可以在一個函數中執行ExecutorService,這樣它的生命周期就限定在一個函數內
在關閉ExecutorService時,可以保存任務待下次繼續執行

處理未捕獲的異常,JDK1.5之前只能通過線程組,可以實現UncaughtExceptionHandler接口,然后賦給Thread
??? Thread#setUncaughtExceptionHandler()
??? 默認情況下線程出現未捕獲的異常,最后是由JVM,也就是本地代碼捕獲到,然后調用線程組的UncaughtExceptionHandler實現
??? 最終是調用System.err,打印出來。

JVM關閉鉤子 Rumtime#addShutdownHook(),最終通過Runtime#halt()關閉虛擬機的

應用程序中,精靈線程不能替代對服務的生命周期恰當、良好的管理。

避免使用Finalizer

?

?

?


第八章 應用線程池
任務執行與執行策略間的隱性耦合
一些任務具有這樣的特征:需要或者排斥某種特定的執行策略。對其他任務具有依賴性的任務,就會要求線程池足夠大,來保證它所依賴任務不必
排隊或者不被拒絕;采用線程限制的任務需要順序地執行。把這些需求都寫入文檔,這樣將來的維護者就不會使用一個與原先相悖的執行策略,而
破壞安全性或活躍度了。

無論何時,你提交了一個非獨立的Executor任務,要明確出現線程饑餓死鎖的可能性,并且在代碼或者配置文件以及其他可以配置Executor的地方,
任何有關池的大小和配置約束都要寫入文檔。

定制線程池的大?。?
Ncpu = CPU的數量
Ucpu = 目標CPU的使用率,0<=Ucpu<=1
W/C = 等待時間與計算時間的比率
為保持處理器達到期望的使用率,最優的池大小等于:
Nthreads = Ncpu * Ucpu * (1+W/C)

newCacheThreadPool()工廠提供了比定長的線程池更好的隊列等待性能,(這個性能差異源自于使用SynchronousQueue取代了
LinkedBlockingQueue)它是Executor的一個很好的默認選擇。處于資源管理的目的,當你需要限制當前任務的數量,一個定長的線程池就是很好
的選擇。就像一個接受網絡客戶端請求的服務器應用程序,如果不進行限制,就會很容易因為過載而遭受攻擊。

飽和策略
線程工廠
擴展ThreadPoolExecutor

?

?

?


第九章 GUI應用程序

?

?

?

?

?

第十章 避免活躍度危險
鎖順序死鎖??? 資源死鎖

兩個線程試圖通過不同的順序獲得多個相同的鎖
如果所有線程以通用的固定秩序獲得鎖,程序就不會出現順序死鎖問題了
動態的鎖順序死鎖?? 加時賽鎖

在持有鎖的時候調用外部方法是在挑戰活躍度的問題。外部方法可能會獲得其他鎖(產生死鎖的風險),或者遭遇嚴重的超時阻塞。當你持有
鎖的時候會延遲其他試圖獲得該鎖的線程。

當調用的方法不需要持有鎖時,這被稱為開放調用。
在程序中盡量使用開放調用。依賴于開放調用的程序,相比于那些在持有鎖的時候還調用外部方法的程序,更容易進行死鎖自由度分析。
線程饑餓死鎖? 單線程化的Executor可能發生

避免死鎖
1.嘗試定時的鎖
2.通過線程轉儲分析死鎖

其他活躍度問題
1.當線程訪問它所需要的資源時卻被永久拒絕,以至于不能再繼續進行,這樣就發生了饑餓
?? 抵制使用線程優先級的誘惑,因為這會增加平臺依賴性,并且可能引起活躍度問題。大多數并發應用程序可以對所有線程使用相同的優先級。
2.弱響應性
3.活鎖是線程中活躍度失敗的另一種形式,盡管沒有被阻塞,線程卻仍然不能繼續,因為它不斷重試相同的操作,卻總是失敗。

?

?

?


第十一章 性能和可伸縮性
可伸縮性指的是:當增加計算資源的時候(比如增加額外CPU數量、內存、存儲器、I/O帶寬),吞吐量和生產量能夠相應地得到改進。

避免不成熟的優化,首先使程序正確,然后再加快--如果他運行的還不夠快。
測試,不要臆測

Amdahl定律:大多數并發程序農耕有陣很多相似之處,有一系列并發和串行化的片段組成。Amdahl定律描述了在一個系統中,基于可并行話和
可串行化的組件各自所占的比重,程序通過額外的計算資源,理論上能夠加速多少。如果F是必須串行化執行的比重,那么Amdahl定律告訴我們,
在一個N處理器的機器中,我們最多可以加速:
Speedup <= 1/(F+ (1-F)/N )

所有的并發程序都有一些串行源,如果你認為你沒有,那么去仔細檢查吧。

不要過分擔心非競爭的同步帶來的開銷?;A的機制已經足夠快了,在這個基礎上,JVM能夠進行額外的優化,大大減少或者消除了開銷,關注那些
真正發生了鎖競爭的區域中性能的優化。

并發程序中,對可伸縮性首要的威脅是獨占的資源鎖。

有三種方式來減少鎖的競爭:
1.減少持有鎖的時間
2.減少請求鎖的頻率
3.或者用協調機制取代獨占鎖,從而允許更強的并發性。

減小鎖的粒度:
分拆鎖
分離鎖(ConcurrentHashMap)

檢測CPU利用率
1.不充足的負載
2.I/O限制
3.外部資源
4.鎖競爭

對象分配通常比同步要更“便宜”

?

?

?


第十二章 測試并發程序
與活躍度測試相關的是性能測試。性能可以通過很多種方式來測量,其中包括:
1.吞吐量:在一個并發任務集里,已完成任務所占的比例。
2.響應性:從請求到完成一些動作之間的延遲(也被稱作等待時間)
3.可伸縮性:增加更多的資源(通常是指CPU),就能提高(或者緩解短缺)吞吐量。

為并發類創建有效的安全測試;其挑戰在于:如何在程序出問題并導致某些屬性極度可能失敗時,簡單地識別出這些受檢測的屬性來,同時不要
人為地讓檢查找錯誤的代碼限制住的并發性。最好能做到在檢測測試的屬性時,不需要任何的同步。

測試應該在多處理器系統上運行,以提高在交替運行的多樣性。但是,多個CPU未必會使測試更加高效。為了能夠最大程度地檢測到時序敏感的
數據競爭的發生機會,應該讓測試中的線程數多于CPU數,這樣在任何給定的時間里,都有一些線程在運行,一些被交換出執行隊列,這樣可以
增加線程間交替行為的隨機性。

編寫有效的性能測試,就需要哄騙優化器不要把你的基準測試當做死代碼而優化掉。這需要每一個計算的結果都要應用在你的程序中--以一種不需要
的同步或真實計算的方式。

測試正確性
? 1.基本的單元測試
? 2.測試阻塞操作
? 3.測試安全性
? 4.測試資源管理 (對JVM內存消耗是否有影響)
? 5.測試性能
? 6.測試響應性??

避免性能測試的陷阱
1.垃圾回收
2.動態編譯(加入一些熱身時間)
3.代碼路徑的非真實取樣
4.不切實際的競爭程度
5.死代碼的消除

使用一些靜態工具如FindBugs來檢測代碼

?

?

?

?

?

第十三章 顯示鎖
Lock、ReentrantLock

性能是一個不斷變化的目標;昨天的基準顯示X比Y更快,這可能已經過時了。

在內部鎖不能夠滿足使用時,ReentrantLock才被作為更高級的工具。當你需要以下高級特性時,才應該使用:可定時的、可輪詢的與
可中斷的鎖獲取操作,公平隊列,或者非塊結構的鎖。則否,請使用synchronized。
讀寫鎖

?

?

?


第十四章 構建自定義的同步工具
條件謂詞是先驗條件的第一站,它再一個操作與狀態轉行之間建立起依賴關系。
將條件謂詞和與之關聯的條件隊列,以及在條件隊列中等待的操作,都寫入文檔。

每次調用wait都會隱式地與特定的條件謂詞相關聯。當調用特定條件謂詞的wait時,調用者必須已經持有了與條件隊列相關的鎖,
這個鎖必須同時還保護著組成條件謂詞的狀態變量。
一個單獨的內部條件隊列可以與多個條件謂詞共同使用。

當使用條件等待(Object.wait()或者Condition.await()):
1.永遠設置一個條件謂詞---一些對象狀態的測試,線程執行前必須滿足它;
2.永遠在調用wait前測試條件謂詞,并且從wait中返回后再次測試;
3.永遠在循環中調用wait;
4.確保構成條件謂詞的狀態變化被鎖保護住,而這個鎖正是條件隊列相關聯的;
5.當調用wait,notify或者notifyAll時,要持有與條件隊列相關的鎖;并且,
6.在檢查條件謂詞之后、開始執行被保護的邏輯之前,不要釋放鎖。

無論何時,當你在等待一個條件,一定要確保有人會在條件謂詞變為真時通知你。

只要同時滿足下述條件后,才能用單一的notify取代notifyAll;
相同的等待者,只有一個條件謂詞與條件隊列相關,每個線程從wait返回后執行相同的邏輯,并且,一進一出,一個隊條件變量的通知,
至多激活一個線程執行。

危險警告:wait、notify和notifyAll在Condition對象的對等體是await、signal和signalAll。但是Condition繼承與Object,這意味著它也有
wait和notify。一定要確保使用了正確的版本-----await和singal。

AbstractQueuedSynchronizer(簡稱AQS)
使用到AQS的類:
ReentrantLock
Semaphore??? CountDownLatch
FutureTask
ReentrantReadWriteLock

當訪問一些有限資源時,就需要同步
1.根據先驗條件,isFull(), isEmpty()將失敗拋給調用者
2.利用輪詢加休眠實現阻塞
3.使用條件隊列來完成阻塞

一個內部條件隊列只能有一個與之相關聯的鎖,
一個Condition和一個單獨的Lock相關聯,而一個Lock可以關聯多個Condition
并且比內部條件隊列有更豐富的特性,中斷/不可中斷,基于時限的等待,公平和非公平


?

?


第十五章 原子變量與非阻塞同步機制
非阻塞算法,比較并交換CAS
硬件的支持: 比較并交換(compare-and-set)
??????????????? 加載鏈接/存儲條件(load-linked/store-conditional)

非阻塞計數器,非阻塞棧(Treiber算法),非阻塞鏈表(Michael-scott算法)
原子變量是“更佳的volatile”
ABA問題


?

?


第十六章 Java存儲模型
重排序
happens-before的法則包括:
程序次序法則:線程中的每個動作A都happens-before于該線程中的每一個動作B,其中,在程序中,所有的動作B都出現在動作A之后。
監視器鎖法則:對一個監視器鎖的解鎖happens-before于每一個后續對同一監視器鎖的加鎖。
volatile變量法則:對volatile域的寫入操作happens-before于每一個后續對同一域的讀操作。
線程啟動法則:在一個線程里,對Thread.start()的調研會happens-before于每一個啟動線程中的動作。
線程終結法則:線程中任何動作都happens-before于其他線程檢測到這個線程已經終結、或者從Thread.join()調用中成功返回,或者
Thread.isAlive()返回false。
中斷法則:一個線程調用另一個線程的interrupt happens-before于被中斷的線程發現中斷(通過拋出InterruptedException,或者調用
isInterruped和interruped。)
終結法則:一個對象的構造函數的結束happens-before于這個對象finalizer的開始。
傳遞性:如果A happens-before于B,且B happens-before于C,則A happens-before于C。

除了不可變對象以外,使用被另一個線程初始化的對象,是不安全的,除非對象的發布是happens-before于對象的消費線程使用它。

初始化安全可以保證,對于正確的創建對象,無論它是如何發布的,所有線程都將看到構造函數設置的final域的值。更進一步,一個正確
創建的對象中,任何可以通過其final域觸及到的變量(比如一個final數組中的元素,或者一個final引用的HashMap里面的內容),也可以保證
對其他線程都是可見的。

DCL失敗的原因
初始化安全性保證只有以通過final域觸及的值,在構造函數完成時是可見的。對于通過非final域觸及的值,或者創建完成后可能改變的值,

必須使用同步來確??梢娦浴?

?

?

?

?

?

網友寫的java并發編程實踐筆記

1, 保證線程安全的三種方法 :
a, 不要跨線程訪問共享變量
b, 使共享變量是 final類型的
c, 將共享變量的操作加上同步

2, 一開始就將類設計成線程安全的 , 比在后期重新修復它 ,更容易 .

3, 編寫多線程程序 , 首先保證它是正確的 , 其次再考慮性能 .

4, 無狀態或只讀對象永遠是線程安全的 .

5, 不要將一個共享變量裸露在多線程環境下 (無同步或不可變性保護 )

6, 多線程環境下的延遲加載需要同步的保護 , 因為延遲加載會造成對象重復實例化

7, 對于 volatile 聲明的數值類型變量進行運算 , 往往是不安全的 (volatile 只能保證可見性 , 不能保證原子性 ).
詳見 volatile 原理與技巧中 , 臟數據問題討論 .

8, 當一個線程請求獲得它自己占有的鎖時 ( 同一把鎖的嵌套使用 ), 我們稱該鎖為可重入鎖 .
在 jdk1.5 并發包中 , 提供了可重入鎖的 java 實現 -ReentrantLock.

9, 每個共享變量 , 都應該由一個唯一確定的鎖保護 .
創建與變量相同數目的 ReentrantLock, 使他們負責每個變量的線程安全 .

10,雖然縮小同步塊的范圍 , 可以提升系統性能 .
但在保證原子性的情況下 , 不可將原子操作分解成多個 synchronized塊 .

11, 在沒有同步的情況下 , 編譯器與處理器運行時的指令執行順序可能完全出乎意料 .
原因是 , 編譯器或處理器為了優化自身執行效率 , 而對指令進行了的重排序 (reordering).

12, 當一個線程在沒有同步的情況下讀取變量 , 它可能會得到一個過期值 , 但是至少它可以看到那個
線程在當時設定的一個真實數值 . 而不是憑空而來的值 . 這種安全保證 , 稱之為 最低限的安全性 (out-of-thin-air safety)

在開發并發應用程序時 , 有時為了大幅度提高系統的吞吐量與性能 , 會采用這種無保障的做法 .
但是針對 , 數值的運算 , 仍舊是被否決的 .

13, volatile 變量 , 只能保證可見性 , 無法保證原子性 .

14, 某些耗時較長的網絡操作或 IO, 確保執行時 , 不要占有鎖 .

15, 發布 (publish) 對象 , 指的是使它能夠被當前范圍之外的代碼所使用 .( 引用傳遞 )
對象逸出 (escape), 指的是一個對象在尚未準備好時將它發布 .

原則 : 為防止逸出 , 對象必須要被完全構造完后 , 才可以被發布 ( 最好的解決方式是采用同步 )

this 關鍵字引用對象逸出
例子 : 在構造函數中 , 開啟線程 , 并將自身對象 this 傳入線程 , 造成引用傳遞 .
而此時 , 構造函數尚未執行完 , 就會發生對象逸出了 .

16, 必要時 , 使用 ThreadLocal變量確保線程封閉性 (封閉線程往往是比較安全的 , 但一定程度上會造成性能損耗 )
封閉對象的例子在實際使用過程中 , 比較常見 , 例如 hibernate openSessionInView機制 , jdbc的 connection機制 .

17, 單一不可變對象往往是線程安全的 (復雜不可變對象需要保證其內部成員變量也是不可變的 )
良好的多線程編程習慣是 : 將所有的域都聲明為 final, 除非它們是可變的

18, 保證共享變量的發布是安全的?
a, 通過靜態初始化器初始化對象 (jls 12.4.2 敘述 , jvm 會保證靜態初始化變量是同步的 )
b, 將對象申明為 volatile 或使用 AtomicReference
c, 保證對象是不可變的
d, 將引用或可變操作都由鎖來保護

19, 設計線程安全的類 , 應該包括的基本要素 :
a, 確定哪些是可變共享變量
b, 確定哪些是不可變的變量
c, 指定一個管理并發訪問對象狀態的策略

20, 將數據封裝在對象內部 , 并保證對數據的訪問是原子的 .
建議采用 volatile javabean 模型或者構造同步的 getter,setter.

21, 線程限制性使構造線程安全的類變得更容易 , 因為類的狀態被限制后 , 分析它的線程安全性時 , 就不必檢查完整的程序 .

22, 編寫并發程序 , 需要更全的注釋 , 更完整的文檔說明 .

23, 在需要細分鎖的分配時 , 使用 java監視器模式好于使用自身對象的監視器鎖 .
前者的靈活性更好 .

Object target = new Object();
// 這里使用外部對象來作為監視器 , 而非 this
synchronized(target) {
// TODO
}

針對 java monitor pattern, 實際上 ReentrantLock的實現更易于并發編程 .
功能上 , 也更強大 .

24,? 設計并發程序時 , 在保證伸縮性與性能折中的前提下 , 優先考慮將共享變量委托給線程安全的類 .
由它來控制全局的并發訪問 .

25, 使用普通同步容器 (Vector, Hashtable) 的迭代器 , 需要外部鎖來保證其原子性 .
原因是 , 普通同步容器產生的迭代器是非線程安全的 .

26, 在并發編程中 , 需要容器支持的時候 , 優先考慮使用 jdk 并發容器?

(ConcurrentHashMap, ConcurrentLinkedQueue, CopyOnWriteArrayList…).

27, ConcurrentHashMap, CopyOnWriteArrayList
并發容器的迭代器 , 以及全范圍的 size(), isEmpty() 都表現出弱一致性 .
他們只能標示容器當時的一個數據狀態 . 無法完整響應容器之后的變化和修改 .

28,? 使用有界隊列 , 在隊列充滿或為空時 , 阻塞所有的讀與寫操作 .? ( 實現生產 – 消費的良好方案 )
BlockQueue 下的實現有 LinkedBlockingQueue 與 ArrayBlockingQueue, 前者為鏈表 , 可變操作頻繁優先考慮 , 后者為數組 , 讀取操作頻繁優先考慮 .
PriorityBlockingQueue 是一個按優先級順序排列的阻塞隊列 , 它可以對所有置入的元素進行排序 ( 實現 Comparator 接口 )

29, 當一個方法 , 能拋出 InterruptedException, 則意味著 , 這個方法是一個可阻塞的方法 , 如果它被中斷 , 將提前結束阻塞狀態 .
當你調用一個阻塞方法 , 也就意味著 , 本身也稱為了一個阻塞方法 , 因為你必須等待阻塞方法返回 .

?

如果阻塞方法拋出了中斷異常 , 我們需要做的是 , 將其往上層拋 , 除非當前已經是需要捕獲異常的層次 .
如果當前方法 , 不能拋出 InterruptedException, 可以使用 Thread.currentThread.interrupt() 方法 , 手動進行中斷 .

?
?

Java并發編程實踐


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦?。?!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 538在线视频二三区视视频 | 欧美在线视频a | 久久亚洲综合网 | 亚洲一区免费在线观看 | 中国特级黄一级真人毛片 | 亚洲在线观看 | 伊人色播 | 欧美激情在线精品一区二区 | 国产毛片一区二区三区精品 | 99国产高清久久久久久网站 | 熊出没之重启未来免费观看 | 欧美日韩亚洲国产精品一区二区 | 天海冀一二三区 | 久久在线免费观看视频 | 欧美久久网 | 日本在线观看不卡免费视频 | 美日韩免费视频 | 亚洲欧美日韩不卡一区二区三区 | 综合亚洲色图 | 日日夜夜精品免费视频 | 亚洲精品www久久久久久久软件 | 欧美伊人久久大香线蕉综合69 | 日本不卡高清免费 | 色丁香六月 | 国产在线19禁免费观看国产 | 欧美一级毛片片aa视频 | 亚欧精品一区二区三区 | 老司机午夜精品99久久免费 | 不卡视频在线观看 | 最新亚洲精品国自产在线 | 中文字幕日韩在线观看 | 国产最新在线视频 | 日本一级大黄毛片一级 | 性欧美精品久久久久久久 | 久久精品国产99久久72 | 奇米第四色888 | 色网站在线播放 | 日韩精品免费一级视频 | 欧美在线视频网 | 色婷婷一区 | 久久精品国产视频 |