顯式鎖(第十三章)

顯式鎖

在Java5.0以前,在協調對共享對象的訪問時可使用的機制只有synchronized和volatile。Java5.0增長了一種新的機制:ReentrantLock。ReentrantLock並非一種替代內置加鎖的方法,而是當內置加鎖機制不適用時,做爲一種可選擇的高級功能。函數

1. Lock接口與ReentrantLock

Lock提供了一種無條件的、可輪詢的、定時的以及可中斷的鎖獲取操做,全部加鎖和解鎖的方法都是顯示的。性能

Lock接口:線程

public interface Lock {
	void lock();
    void lockInterruptibly throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

ReentrantLock實現了Lock接口,並提供了與synchronized相同的互斥性和內存可見性。code

Lock接口的使用形式:對象

Lock lock = new ReentrantLock();
...
lock.lock();
try {
	//更新對象狀態
    //捕獲異常,並在必要時恢復不變性條件
} finally {
	lock.unlock();
}
使用Lock時,必須在finally塊中釋放鎖,不然,若是被保護的代碼中拋出了異常,那麼這個鎖永遠都沒法釋放。這就是ReentrantLock不能徹底替代synchronized的緣由:它更加危險,由於程序的執行控制離開被保護的代碼塊時,不會自動清除鎖。

2. 輪詢鎖與定時鎖

可定時的與可輪訓的鎖獲取模式是由tryLock方式實現的,與無條件的鎖獲取模式相比,它具備更完善的錯誤恢復機制。接口

可定時的與可輪詢的鎖可以避免死鎖的發生。若是不能得到全部須要的鎖,那麼可使用可定時的或可輪詢的鎖獲取方式,從而使你從新得到控制權,它會釋放已經得到的鎖,而後從新嘗試獲取全部鎖。

在實現具備時間限制的操做時,定時鎖可以提供一個時限,若是操做不能在指定的時間內給出結果,那麼就會使程序提早結束。隊列

3. 可中斷的鎖獲取操做

Java中,請求內置鎖時不能響應中斷。Lock的lockInterruptibly方法可以在鎖的同時保持對中斷的響應,且因爲它包含在Lock中,所以無須建立其餘類型的不可中斷阻塞機制。事件

...
lock.lockInterruptibly();
...

定時的tryLock一樣能響應中斷,所以當須要實現一個定時的和可中斷的鎖獲取操做時,但是使用tryLock方法。內存

4. 公平性

在ReentrantLock的構造函數中提供了兩種公平性選擇:非公平(默認)、公平。 在公平的鎖上,線程按照它們發出請求的順序來得到鎖,但在非公平的鎖上,則容許「插隊」:當一個線程請求非公平的鎖時,若是在發出請求的同時該鎖的狀態變爲可用,那麼這個線程將跳過隊列中全部的等待線程並得到這個鎖。資源

在大多數狀況下,非公平鎖的性能要高於公平鎖的性能。

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

與默認的ReentrantLock同樣,內置鎖並不會提供肯定的公平性保證,但在大多數狀況下,在鎖實現上實現統計上的公平性保證已經足夠了。Java語言規範並無要求JVM以公平的方式來實現內置鎖,而在各類JVM中也沒有這樣作。ReentrantLock並無進一步下降鎖的公平性,而只是使一些已經存在的內容更明顯。

5.在synchronized和ReentrantLock之間進行選擇

  • ReentrantLock的優勢: ReentrantLock在加鎖和內存上提供的語義與內置鎖相同,此外它還提供了一些其餘功能,包括定時的鎖等待、可中斷的鎖等待、公平性,以及實現非塊結構的加鎖

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

    二者之間的選擇:
      當須要一些高級功能時才應該使用ReentrantLock,這些功能包括:可定時的、可輪訓的與可中斷的鎖獲取操做,公平隊列,以及非塊結構的鎖。不然,仍是應該優先使用synchronized。

6. 讀-寫鎖

一個資源能夠被多個讀操做訪問,或者被一個寫操做訪問,但二者不能同時進行。 要讀取由ReadWriteLock保護的數據,必須首先得到讀取鎖,當須要修改ReadWriteLock保護的數據時,必須首先得到寫入鎖。儘管這兩個鎖看上去是彼此獨立的,但讀取鎖和寫入鎖只是讀-寫鎖對象的不一樣視圖。

public interface ReadWriteLock {
	Lock readLock();
    Lock writeLock();
}

ReentranReadWriteLock爲這兩種鎖都提供了可重入的加鎖語義。與ReentrantLock相似,ReentrantReadWriteLock在構造時也能夠選擇是一個非公平的鎖(默認)仍是一個公平的鎖。 在公平的鎖中,等待事件最長的線程將優先得到鎖。若是這個鎖由讀線程持有,而另外一個線程請求寫入鎖,那麼其餘讀線程都不能得到讀取鎖,直到寫線程使用完而且釋放了寫入鎖。 在非公平的鎖中,線程得到訪問許可的順序是不肯定的。寫線程降級爲讀線程是能夠的,但從讀線程升級爲寫線程則是不能夠的(這樣作會致使死鎖)。

相關文章
相關標籤/搜索