天天看點

顯式鎖(第十三章)

在Java5.0之前,在協調對共享對象的通路時可以使用的機制隻有synchronized和volatile。Java5.0增加了一種新的機制:ReentrantLock。ReentrantLock并不是一種替代内置加鎖的方法,而是當内置加鎖機制不适用時,作為一種可選擇的進階功能。

Lock提供了一種無條件的、可輪詢的、定時的以及可中斷的鎖擷取操作,所有加鎖和解鎖的方法都是顯示的。

Lock接口:

ReentrantLock實作了Lock接口,并提供了與synchronized相同的互斥性和記憶體可見性。

Lock接口的使用形式:

可定時的與可輪訓的鎖擷取模式是由tryLock方式實作的,與無條件的鎖擷取模式相比,它具有更完善的錯誤恢複機制。

在實作具有時間限制的操作時,定時鎖能夠提供一個時限,如果操作不能在指定的時間内給出結果,那麼就會使程式提前結束。

Java中,請求内置鎖時不能響應中斷。Lock的lockInterruptibly方法能夠在鎖的同時保持對中斷的響應,且由于它包含在Lock中,是以無須建立其他類型的不可中斷阻塞機制。

定時的tryLock同樣能響應中斷,是以當需要實作一個定時的和可中斷的鎖擷取操作時,可是使用tryLock方法。

在ReentrantLock的構造函數中提供了兩種公平性選擇:非公平(預設)、公平。

在公平的鎖上,線程按照它們送出請求的順序來獲得鎖,但在非公平的鎖上,則允許“插隊”:當一個線程請求非公平的鎖時,如果在送出請求的同時該鎖的狀态變為可用,那麼這個線程将跳過隊列中所有的等待線程并獲得這個鎖。

當持有鎖的時間相對較長,或者請求鎖的平均時間間隔較長,那麼應該使用公平鎖。在這些情況下,“插隊”帶來的吞吐量提升(當鎖處于可用狀态時,線程卻還處于被喚醒的過程中)則可能不會出現。

與預設的ReentrantLock一樣,内置鎖并不會提供确定的公平性保證,但在大多數情況下,在鎖實作上實作統計上的公平性保證已經足夠了。Java語言規範并沒有要求JVM以公平的方式來實作内置鎖,而在各種JVM中也沒有這樣做。ReentrantLock并沒有進一步降低鎖的公平性,而隻是使一些已經存在的内容更明顯。

ReentrantLock的優點:

ReentrantLock在加鎖和記憶體上提供的語義與内置鎖相同,此外它還提供了一些其他功能,包括定時的鎖等待、可中斷的鎖等待、公平性,以及實作非塊結構的加鎖。

ReentrantLock的缺點:

ReentrantLock的危險性比同步機制要高,如果忘記在finally塊中調用unlock,那麼就有可能出現問題。

一個資源可以被多個讀操作通路,或者被一個寫操作通路,但兩者不能同時進行。

要讀取由ReadWriteLock保護的資料,必須首先獲得讀取鎖,當需要修改ReadWriteLock保護的資料時,必須首先獲得寫入鎖。盡管這兩個鎖看上去是彼此獨立的,但讀取鎖和寫入鎖隻是讀-寫鎖對象的不同視圖。

ReentranReadWriteLock為這兩種鎖都提供了可重入的加鎖語義。與ReentrantLock類似,ReentrantReadWriteLock在構造時也可以選擇是一個非公平的鎖(預設)還是一個公平的鎖。

在公平的鎖中,等待事件最長的線程将優先獲得鎖。如果這個鎖由讀線程持有,而另一個線程請求寫入鎖,那麼其他讀線程都不能獲得讀取鎖,直到寫線程使用完并且釋放了寫入鎖。

在非公平的鎖中,線程獲得通路許可的順序是不确定的。寫線程降級為讀線程是可以的,但從讀線程更新為寫線程則是不可以的(這樣做會導緻死鎖)。