《Java併發程式設計實踐——第三章(共享物件)》
共享物件##
編寫正確的併發程式的關鍵在於對共享的、可變的物件狀態進行訪問管理。
上一章使用同步來避免多個執行緒在同一時間訪問同一資料。
同步還有另外的方面:記憶體可見性。
3.1 可見性
public class NoVisibility { private static boolean ready; private static int number; private static class ReaderThread extends Thread{ @Override public void run() { while (!ready) { Thread.yield(); } System.out.println(number); } } public static void main(String[] args) { new ReaderThread().start(); number = 42; ready = true; } }
只要資料需要被跨執行緒共享,就進行恰當的同步。
3.1.1 過期資料
NoVisibility演示了一種沒有恰當同步的程式,它能夠引起意外結果:過期資料。
當執行緒檢查read變數時,它可能看到一個過期的值。
非執行緒安全的可變整數訪問器:
執行緒安全的可變整數訪問器:
3.1.2 非原子的64位操作
最低限的安全性應用於所有的變數,除了一個例外:沒有宣告為volatile 的64位數值變數(double和long)。Java允許64位的讀或寫劃分為兩個32位才值。如果讀寫發生在不同的執行緒,非volatile 的64位數可能會出現一個值的高32位和另一個值的低32位。
3.1.3 鎖和可見性
當訪問一個共享變數時,為什麼要求所有執行緒由同一個鎖同步。
為了保證一個執行緒崔數值進行的寫入,其他執行緒也可見。
3.1.3 Volatile
弱同步形式:Volatile變數
Volatile確保對一個變數的更新以可預見的方式告知其他執行緒。
volatile的典型應用:狀態檢查標記。
加鎖可以保證可見性與原子性;volatile變數只能保證可見性。
3.2 釋出與逸出
釋出一個物件:使它能夠在當前範圍之外的程式碼所使用。一個物件在尚未準備好時就將它釋出,被稱作逸出。
釋出物件:
允許內部可變的逸出:
隱式地允許this引用逸出:
內部類的例項包含了對封裝例項隱含的引用。
3.2.1 安全構建的實踐
不要讓this引用在構造器期間逸出。
在建構函式中建立執行緒會導致this在構造期間逸出。
如果想在建構函式中註冊監聽器或啟動執行緒,可以使用一個私有的構造方法和一個公共的工廠方法。
3.3 執行緒封閉
訪問共享的、可變的資料要求使用同步。一個避免同步的方式就是使用不共享資料。
執行緒封閉是實現執行緒安全的最簡單方式之一。
另一個常見使用執行緒限制的是應用池化的JDBC Connection物件。執行緒總是從池中獲得一個Connection物件,並且用它處理單一請求,最後把它歸還。
3.3.1 Ad-hoc 執行緒限制
Ad-hoc 執行緒限制是指維護執行緒限制性的任務全部落在實現上這種情況。
未經過設計而得到的執行緒封閉行為。
3.3.2 棧限制
在棧限制中,只有通過本地變數才可以觸及物件。
類似區域性變數,只存在當前執行執行緒中。
3.3.3 ThreadLocal
ThreadLocal 提供了get和set方法,可以將數值關聯線上程上。
ThreadLocal 通常用於防止在基於可變的單體或者全域性變數的設計中。
3.4 不可變性
為了滿足同步的需要,另一種方法是使用不可變物件。
不可變物件永遠是執行緒安全的。
3.4.1 Final域
Final對建立不可變物件提供了支援。
Final域是不能被修改的(指向的物件是可變的,這個物件仍然可被修改)。
3.4.2 使用volatile釋出不可變物件
使用可變的容器,就必須使用鎖以確保原子性;使用不可變物件,一旦一個物件獲取了它的引用,用於不用擔心其他執行緒會修改它的狀態。
3.5 安全釋出
由於可見性的問題,容器還是會在其他執行緒中被設定為一個不一致狀態。這種不正確的釋出會導致其他執行緒可以觀察到“區域性建立物件”。
3.5.1 不正確釋出:當好物件變壞時
因為沒有同步Holer對其他執行緒可見,所以稱Holer是“非正確釋出的”。
沒有同步Holer會導致兩種錯誤:其他執行緒會看到Holer域的過期值;
3.5.2 不可變物件與初始化安全性
Java儲存模型為共享不可變物件提供了特殊的初始化安全性保證。
3.5.3 安全釋出模式
靜態初始化器建立物件:
public static Holder holder = newHolder(42);
執行緒安全庫中的容器提供瞭如下的執行緒安全保證: