併發編程

 DougLea:《Concurrent Programming in Java:Design Principles and Patterns》

 

1、In Action

(1)執行的可能路徑

Java代碼會變成字節碼指令。

對於指令系列中有N個指令和T個線程,沒有循環或條件分支的簡單情況,總的執行路徑數量等於(NT)!    /   (N!)的T次冪

 

根據java內存模型,32位值的賦值操作是不可中斷的。如 int a = 2;

根據JVM規約,64位值的賦值需要兩次32位賦值。

 

框架——每個方法調用都需要一個框架。該框架包括返回地址、傳入方法的參數,以及方法中定義的本地變量。這是定義一個調用堆棧的標準技術。現代編程語言用來實現基本函數調用和遞歸調用。

本地變量——方法作用範圍內定義的每個變量。所有非靜態方法至少有一個變量this,代表當前對象,即接收導致方法調用的(當前線程內)大多數最新消息的對象。

運算對象棧——java虛擬機中的許多指令都有參數。運算對象棧是放置參數的地方。

 

操作對象都處理對於方法而言是本地的信息。故多個線程之間並無衝突。

 

理解線程之間如何相互干涉的,並不一定要精通字節碼。有必要儘量理解內存模型,明白什麼是安全的,什麼是不安全的。

必須要知道:

a、什麼地方有共享對象/值;

b、哪些代碼會導致併發讀/寫問題;

c、如何防止這種併發問題發生。

 

(2)Executor框架

Executor框架支持利用線程池進行復雜的執行。Executor框架將線程放到池中,自動調整其大小,並在必要時重建線程。它支持future,一種通用的併發編程構造。Executor能與實現了Runnable的類協同工作,也能與實現了Callable接口的類協同工作。Callback看來就像是Runnable,但它能返回一個結果。

 

(3)

 

2、TIPS

(1)要保持併發系統整潔,應該將線程管理代碼約束於少數幾處控制良好的地方。

爲每個職責創建單獨的類

 

(2)非鎖定的解決方案

java5虛擬機利用現代處理器支持可靠、非鎖定更新的設計優點。

如java5有一系列的新類,如AtomicBoolean、AtomicInteger、AtomicReference等。可以使用非鎖定的手段。

 

現代處理器有比較交換(CAS)操作,這種操作類似於數據庫中的樂觀鎖定,而其同步版本則類似於保守鎖定。

 

關鍵字synchronized總是要求上鎖,即使第二個線程並不更新同一值時也是如此。儘管這種固有鎖的性能一直在提升,但代價仍然昂貴。

 

非上鎖的版本假定多個線程通常並不頻繁修改同一個值。

CAS的操作是原子的。邏輯上是這樣:當某個方法試圖更新一個共享變量,CAS操作就會驗證要賦值的變量是否保有上一次的已知值。若是,就修改變量值。若不是,則不會碰變量,因爲另一個線程正在試圖更新變量值。要更新數據的方法(通過CAS操作)查看是否修改並持續嘗試。

 

(3)非線程安全類

a、數據庫連接

b、java.util中的容器

c、servlet

線程安全的集合:java.util.concurrent中的集合,如ConcurrentHashMap。

 

(4)出現錯誤時,有兩種解決方法

a、基於客戶代碼的鎖定

b、基於服務端的鎖定(建議)——修改服務端代碼解決問題,同時也修改了客戶代碼。若無法修改服務端代碼,可以使用適配器模式修改API,添加鎖定。

更好的方法是使用線程安全的集合和擴展接口

 

(5)死鎖

死鎖的發生需要4個條件:

a、互斥:當多個線程需要使用同一資源,而此資源無法在同一時間爲多個線程所用

b、上鎖及等待:當某個線程獲取一個資源,在獲取到其他全部所需資源並完成其工作之前,不會釋放這個資源;

c、無搶先機制:線程無法從其他線程處奪取資源。一個線程持有資源時,其它線程獲得這個資源的唯一手段就是等待該線程釋放資源;

d、循環等待

這4個條件都是死鎖必需的。只要其中一個不滿足,死鎖就不會發生。

 

避免死鎖的一種策略是規避互斥條件。可以使用允許同時使用的資源,如AtomicInteger

線程1同時需要資源1和資源2、線程2同時需要資源2和資源1,只要強制線程1和線程2以同樣次序分配資源,循環等待就不會發生。

 

有許多避免死鎖的方法,有些會導致飢餓,另外一些會導致對CPU能力的大量耗費和降低響應率。

 

將解決方案中與線程相關的部分分割出來,再加以調整和試驗。

 

小心記錄在何種條件下測試失敗

 

測試線程代碼的工具支持:

IBM提供ConTest的工具,它能對類進行裝置,令非線程安全代碼更有可能失敗。

 

(6)

 

3、PS

(1)系統在什麼地方耗費時間

a、I/O——使用套接字、連接到數據庫、等待虛擬內存交換等;

b、處理器——數值計算、正則表達式處理、垃圾回收等。

若代碼運行速度主要和處理器相關,增加處理器硬件就能提升吞吐量。CPU運算週期是有上限的,只是增加線程的話並不會提升受處理器限制的代碼的速度

若吞吐量與IO有關,則併發編程能提升運行效率。當系統的某個部分在等待IO,另一部分就可以利用等待的時間處理其他事,從而有效利用了CPU能力。

 

IO操作不耗費處理器能力

 

(2)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章