在Java5.0以前,在協調對共享對象的訪問時可使用的機制只有synchronized和volatile。Java5.0增長了一種新的機制:ReentrantLock。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的緣由:它更加危險,由於程序的執行控制離開被保護的代碼塊時,不會自動清除鎖。
可定時的與可輪訓的鎖獲取模式是由tryLock方式實現的,與無條件的鎖獲取模式相比,它具備更完善的錯誤恢復機制。接口
可定時的與可輪詢的鎖可以避免死鎖的發生。若是不能得到全部須要的鎖,那麼可使用可定時的或可輪詢的鎖獲取方式,從而使你從新得到控制權,它會釋放已經得到的鎖,而後從新嘗試獲取全部鎖。
在實現具備時間限制的操做時,定時鎖可以提供一個時限,若是操做不能在指定的時間內給出結果,那麼就會使程序提早結束。隊列
Java中,請求內置鎖時不能響應中斷。Lock的lockInterruptibly方法可以在鎖的同時保持對中斷的響應,且因爲它包含在Lock中,所以無須建立其餘類型的不可中斷阻塞機制。事件
... lock.lockInterruptibly(); ...
定時的tryLock一樣能響應中斷,所以當須要實現一個定時的和可中斷的鎖獲取操做時,但是使用tryLock方法。內存
在ReentrantLock的構造函數中提供了兩種公平性選擇:非公平(默認)、公平。 在公平的鎖上,線程按照它們發出請求的順序來得到鎖,但在非公平的鎖上,則容許「插隊」:當一個線程請求非公平的鎖時,若是在發出請求的同時該鎖的狀態變爲可用,那麼這個線程將跳過隊列中全部的等待線程並得到這個鎖。資源
在大多數狀況下,非公平鎖的性能要高於公平鎖的性能。
當持有鎖的時間相對較長,或者請求鎖的平均時間間隔較長,那麼應該使用公平鎖。在這些狀況下,「插隊」帶來的吞吐量提高(當鎖處於可用狀態時,線程卻還處於被喚醒的過程當中)則可能不會出現。
與默認的ReentrantLock同樣,內置鎖並不會提供肯定的公平性保證,但在大多數狀況下,在鎖實現上實現統計上的公平性保證已經足夠了。Java語言規範並無要求JVM以公平的方式來實現內置鎖,而在各類JVM中也沒有這樣作。ReentrantLock並無進一步下降鎖的公平性,而只是使一些已經存在的內容更明顯。
ReentrantLock的優勢: ReentrantLock在加鎖和內存上提供的語義與內置鎖相同,此外它還提供了一些其餘功能,包括定時的鎖等待、可中斷的鎖等待、公平性,以及實現非塊結構的加鎖。
ReentrantLock的缺點: ReentrantLock的危險性比同步機制要高,若是忘記在finally塊中調用unlock,那麼就有可能出現問題。
二者之間的選擇: 當須要一些高級功能時才應該使用ReentrantLock,這些功能包括:可定時的、可輪訓的與可中斷的鎖獲取操做,公平隊列,以及非塊結構的鎖。不然,仍是應該優先使用synchronized。
一個資源能夠被多個讀操做訪問,或者被一個寫操做訪問,但二者不能同時進行。 要讀取由ReadWriteLock保護的數據,必須首先得到讀取鎖,當須要修改ReadWriteLock保護的數據時,必須首先得到寫入鎖。儘管這兩個鎖看上去是彼此獨立的,但讀取鎖和寫入鎖只是讀-寫鎖對象的不一樣視圖。
public interface ReadWriteLock { Lock readLock(); Lock writeLock(); }
ReentranReadWriteLock爲這兩種鎖都提供了可重入的加鎖語義。與ReentrantLock相似,ReentrantReadWriteLock在構造時也能夠選擇是一個非公平的鎖(默認)仍是一個公平的鎖。 在公平的鎖中,等待事件最長的線程將優先得到鎖。若是這個鎖由讀線程持有,而另外一個線程請求寫入鎖,那麼其餘讀線程都不能得到讀取鎖,直到寫線程使用完而且釋放了寫入鎖。 在非公平的鎖中,線程得到訪問許可的順序是不肯定的。寫線程降級爲讀線程是能夠的,但從讀線程升級爲寫線程則是不能夠的(這樣作會致使死鎖)。