1. 程式人生 > >《Java併發程式設計實踐——第三章(共享物件)》

《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);

執行緒安全庫中的容器提供瞭如下的執行緒安全保證:
在這裡插入圖片描述

3.5.4 高效不可變物件
3.5.5 可變物件

在這裡插入圖片描述

3.5.6 安全地共享物件

在這裡插入圖片描述