前言
下面簡單總結學習Java并發的筆記,關于如何利用面向對象思想寫好并發程式的建議。面向對象的思想和并發程式設計屬于兩個領域,但是在Java中這兩個領域卻可以融合到一起。在Java語言中,面向對象程式設計的思想能夠讓并發程式設計變得更加簡單。下面将從封裝共享變量、識别共享變量間的限制條件和制定并發通路政策三方面介紹如何使用面向對象思想去指導編寫并發程式。
封裝共享變量
在并發程式設計中,格外關心的一個重點便是多線程對共享變量的通路問題。我們需要控制好對共享變量的通路接口。面向對象就有一個非常好的特性:封裝(将屬性和實作細節封裝在對象内部),外界隻能通過目标對象提供的公共方法來間接通路這些内部屬性。使用面向對象的這個特性,就可以非常輕松地掌控共享變量的通路路徑。
利用面向對象思想編寫并發程式的思路:将共享變量作為對象屬性封裝在内部,對所有公共方法定制并發通路政策。
例如,下面的計數器程式。計數器程式共享變量隻有一個,即
value
,我們把它作為
Counter
類的屬性,并且将兩個公共方法
get()
和
set()
聲明為同步方法,這樣Counter類就成為了一個線程安全的類了。
public class Counter {
private long value;
synchronized long get(){
return value;
}
synchronized long addOne(){
return ++value;
}
}
識别共享變量之間的限制條件
識别共享變量之間的限制條件十分重要,因為這會影響到并發通路政策的定制。
下面舉例說明。
在庫存管理中有個合理庫存的概念,庫存量不能太高,也不能太低,它有一個上限和一個下限。下面使用代碼說明。
在
SafeVM
中,聲明了兩個成員變量
upper
lower
,分别代表了庫存上限和下限,我們使用原子類
AtomLong
來定義這兩個變量。由于原子類是線程安全的,是以這兩個成員變量的set()方法就不需要同步。
public class SafeWM {
// 庫存上限
private final AtomicLong upper = new AtomicLong(0);
// 庫存下限
private final AtomicLong lower = new AtomicLong(0);
// 設定庫存上限
void setUpper(long v){
upper.set(v);
}
// 設定庫存下限
void setLower(long v){
lower.set(v);
}
// 省略其他業務代碼
}
但是,我們需要注意,兩個共享變量之間是有一個限制條件的:庫存下限要小于庫存上限。
于是我們就要加入參數校驗,我們在方法
setUpper()
和方法
setLower()
中加入檢驗語句:
// 設定庫存上限
void setUpper(long v){
// 檢查參數合法性
if (v < lower.get()) {
throw new IllegalArgumentException();
}
upper.set(v);
}
// 設定庫存下限
void setLower(long v){
// 檢查參數合法性
if (v > upper.get()) {
throw new IllegalArgumentException();
}
lower.set(v);
}
看似上面的代碼沒有什麼問題,但是仔細分析一下,便可以發現其實存在競态條件。(校驗的結果依賴線程的執行順序)
例如,庫存初始的上限和下限分别為(2,10)。線程 A 調用
setUpper(5)
将上限設定為 5,線程 B 調用
setLower(7)
将下限設定為 7。線程A和線程B同時執行,會發現線程A和線程B都可以同時通過校驗,導緻最終庫存為(7,5)。
線程A執行時,下限還沒有被線程 B 設定,還是 2,而 5>2;線程B執行時,上限還沒有被線程 A 設定,還是 10,而 7<10。
在沒有識别出庫存下限要小于庫存上限這個限制條件之前,我們制定的并發通路政策是利用原子類,但是這個政策,完全不能保證庫存下限要小于庫存上限這個限制條件。
是以,在設計階段,我們一定要識别出所有共享變量之間的限制條件,如果限制條件識别不足,很可能導緻制定的并發通路政策南轅北轍。
制定并發通路政策
指定并發通路政策,從方案思想上來看,可以從以下三個方面入手:(在前一篇部落格的小結中也提到過)
-
避免共享
上篇文章介紹的線程封閉技術。
-
不變模式
例如
模式,Actor
模式以及函數式程式設計的基礎都是不變模式。CSP
-
同步機制和并發容器synchronized
除了以上方案思想,還有一些宏觀原則需要了解。
-
優先使用成熟的工具類
使用已經設計好的工具類,避免重複造輪子。
-
迫不得已才使用低級的同步原語
低級的同步原語主要指的是
、synchronized
Lock
等,雖然看上去簡單,但使用起來還是要萬分小心。Semaphore
-
避免過早優化
先保證線程安全性,再考慮優化性能。
小結
主要是學習參考[1]時的學習總結筆記,沒有加入太多自己的思考或者補充點,(◞‸◟ )積累還不夠。
要好好撸起袖子加油幹!( ̄^ ̄)ゞ
參考:
[1]極客時間專欄王寶令《Java并發程式設計實戰》
每天進步一點點,不要停止前進的腳步~