java核心技術卷I-同步(二)

同步阻塞

每一個 Java 對象有一個鎖。線程可以通過調用同步方法獲得鎖。還有另一種機制可以獲得鎖,通過進入一個同步阻塞。

synchronized (obj) // this is the syntax for a synchronized block
{
	critical section
}

於是它獲得 Obj 的鎖

監視器概念

鎖和條件是線程同步的強大工具,但是,嚴格地講,它們不是面向對象的。多年來,研究人員努力尋找一種方法,可以在不需要程序員考慮如何加鎖的情況下,就可以保證多線程的安全性。最成功的解決方案之一是監視器(monitor),
監視器具有如下特性:

監視器是隻包含私有域的類。
每個監視器類的對象有一個相關的鎖。
使用該鎖對所有的方法進行加鎖。換句話說,如果客戶端調用obj.method(), 那 麼 obj對象的鎖是在方法調用開始時自動獲得,並且當方法返回時自動釋放該鎖。因爲所有的域是私有的,這樣的安排可以確保一個線程在對對象操作時, 沒有其他線程能訪問該域。
該鎖可以有任意多個相關條件

Java 設計者以不是很精確的方式採用了監視器概念, Java 中的每一個對象有一個內部的鎖和內部的條件。如果一個方法用 synchronized 關鍵字聲明,那麼,它表現的就像是一個監視器方法。通過調用 wait/notifyAll/notify

Volatile域

volatile 關鍵字爲實例域的同步訪問提供了一種免鎖機制。如果聲明一個域爲 volatile ,那麼編譯器和虛擬機就知道該域是可能被另一個線程併發更新的。
Volatile 變量不能提供原子性。

public void flipDone() { done = !done; } // not atomic
count ++;

final 變量

除非使用鎖或 volatile 修飾符,否則無法從多個線程安全地讀取一個域。還有一種情況可以安全地訪問一個共享域, 即這個域聲明爲 final 時。

final Map<String, Double> accounts = new HashMap<>();

其他線程會在構造函數完成構造之後纔看到這個 accounts 變量。
如果不使用 final,就不能保證其他線程看到的是 accounts 更新後的值,它們可能都只是看到 null , 而不是新構造的 HashMap。
當然,對這個映射表的操作並不是線程安全的。如果多個線程在讀寫這個映射表,仍然
需要進行同步。

原子性

假設對共享變量除了賦值之外並不完成其他操作,那麼可以將這些共享變量聲明爲volatile。
java.util.concurrent.atomic 包中有很多類使用了很高效的機器級指令(而不是使用鎖) 來保證其他操作的原子性。 例如, Atomiclnteger 類提供了方法 incrementAndGet 和decrementAndGet, 它們分別以原子方式將一個整數自增或自減。例如,可以安全地生成一個數值序列。

public static AtomicLong nextNumber = new AtomicLong() ;
// In some thread...
long id = nextNumber.increinentAndGet():

incrementAndGet 方法以原子方式將 AtomicLong 自增, 並返回自增後的值。也就是說,獲得值、 增1並設置然後生成新值的操作不會中斷。可以保證即使是多個線程併發地訪問同一個實例,也會計算並返回正確的值。

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