189
汽車大全
顯式鎖(第十三章)
顯式鎖
在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相同的互斥性和內存可見性。
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在構造時也可以選擇是一個非公平的鎖(默認)還是一個公平的鎖。
在公平的鎖中,等待事件最長的線程將優先獲得鎖。如果這個鎖由讀線程持有,而另一個線程請求寫入鎖,那麼其他讀線程都不能獲得讀取鎖,直到寫線程使用完並且釋放了寫入鎖。
在非公平的鎖中,線程獲得訪問許可的順序是不確定的。寫線程降級為讀線程是可以的,但從讀線程升級為寫線程則是不可以的(這樣做會導致死鎖)。
最後更新:2017-11-04 18:33:51