ReentrantLock并不是一種代替內(nèi)置加鎖的方法,而是當(dāng)內(nèi)置加鎖機(jī)制不適用時(shí)的一種可選擇的高級(jí)功能癌幕。
1. Lock與ReentrantLock
Lock提供了一種無條件的兆蕉、可輪詢的、定時(shí)的以及可中斷的鎖獲取操作芋酌,所以加鎖操作和解鎖的方法都是顯式的。
public interface Lock{
void lock();
void lockInterruptibly() throws InterruptrdException;
boolean tryLock();
boolean tryLock(long timeout,TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
ReentrantLock實(shí)現(xiàn)了Lock接口雁佳,并提供了與synchronized相同的互斥性和內(nèi)存可見性脐帝。它還支持在Lock中定義的所有的獲取鎖的模式,并且與synchronized相比糖权,它還為處理鎖的不可用性問題提供了更高的靈活性堵腹。
內(nèi)置鎖在功能上的局限性:
無法中斷一個(gè)正在等待獲取鎖的線程,或者無法在請(qǐng)求一個(gè)鎖時(shí)無限的等下去...
- 優(yōu)點(diǎn):由于內(nèi)置鎖必須在獲取該鎖的代碼塊中釋放星澳,從而簡(jiǎn)化了編碼工作疚顷,并且與異常交互的很好
- 無法實(shí)現(xiàn)非阻塞結(jié)構(gòu)的加鎖規(guī)則。
Lock接口的標(biāo)準(zhǔn)使用方式:
Lock lock=new ReentrantLock();
...
lock.lock();
try{
//更新對(duì)象狀態(tài)
//補(bǔ)貨異常禁偎,在必要時(shí)回復(fù)不變性條件
} finally{
lock.unlock;
}
該形式比內(nèi)置鎖復(fù)雜一些荡含,必須在finally中釋放鎖。否則會(huì)導(dǎo)致一定的危險(xiǎn)届垫。這個(gè)也是ReentrantLock不能完全替代synchronized的原因释液,容易忘記在fianlly中釋放鎖。
1.1 輪詢鎖與定時(shí)鎖
這兩種鎖的獲取模式都是由tryLock方法實(shí)現(xiàn)的装处,具有更完善的錯(cuò)誤恢復(fù)機(jī)制误债。并且可以避免死鎖的發(fā)生:如果不能獲得所有需要的鎖,可以使用定時(shí)或者輪詢的鎖獲取方式妄迁,從而使你可以重新獲得控制權(quán)寝蹈,它會(huì)釋放已經(jīng)獲得的鎖,然后重新嘗試獲取所有的鎖登淘。
當(dāng)在帶有時(shí)間限制的操作中調(diào)用一個(gè)阻塞方法時(shí)箫老,她會(huì)根據(jù)剩余時(shí)間來提供一個(gè)時(shí)限,如果操作不能在相應(yīng)的時(shí)間內(nèi)給出結(jié)果黔州,程序就會(huì)提前結(jié)束耍鬓。然鵝內(nèi)置鎖很難實(shí)現(xiàn)帶有時(shí)間限制的操作阔籽。
1.2 可中斷的鎖獲取操作
lockInterrutibly方法能夠在獲得鎖的同時(shí)保持對(duì)中斷的響應(yīng),并且由于它處于Lock中牲蜀,因此無需創(chuàng)建其他類型的不可中斷阻塞機(jī)制笆制。
2. 公平性
在ReentrantLock的構(gòu)造函數(shù)中提供了兩種公平性選擇:
-
創(chuàng)建一個(gè)公平的鎖
在公平的鎖上,線程將按照它們發(fā)出請(qǐng)求的順序來獲得鎖涣达。
在公平的鎖中在辆,如果有一個(gè)線程持有這個(gè)鎖或者有其他線程在隊(duì)列中等待這個(gè)鎖,那么新發(fā)出請(qǐng)求的線程將被放入隊(duì)列中度苔。
適用于持鎖時(shí)間相對(duì)較長(zhǎng)或者請(qǐng)求鎖的平均時(shí)間間隔較長(zhǎng)的情況匆篓。 -
創(chuàng)建一個(gè)非公平的鎖
在非公平的鎖上,允許插隊(duì):當(dāng)一個(gè)線程請(qǐng)求非公平的鎖時(shí)寇窑,如果再發(fā)出請(qǐng)求的同時(shí)鸦概,該鎖的狀態(tài)變?yōu)榭捎茫敲丛摼€程將跳過所有的等待線程并獲得這個(gè)鎖疗认。
在非公平的鎖中,只有當(dāng)鎖被某個(gè)線程持有時(shí)伏钠,新發(fā)出的線程請(qǐng)求才會(huì)被放入到隊(duì)列中横漏。
在大多數(shù)情況下,非公平的鎖的性能都要高于公平鎖的性能熟掂。
在競(jìng)爭(zhēng)激烈的情況下缎浇,非公平鎖的性能高于公平鎖性能的原因之一有:
在恢復(fù)一個(gè)被掛起的線程與該線程真正被執(zhí)行之間存在著嚴(yán)重的延遲!
E.G.線程A持有一個(gè)鎖赴肚,并且線程B請(qǐng)求這個(gè)鎖素跺,因此B被掛起。A釋放鎖的時(shí)候誉券,B被喚醒指厌,并且嘗試再次獲取這個(gè)鎖。與此同時(shí)踊跟,C也在請(qǐng)求這個(gè)鎖踩验,那么C很有可能在B被完全喚醒之前獲得、使用以及釋放了這個(gè)鎖商玫。從而提高了吞吐量呀箕憾!
3. 在synchronized和ReentrantLock之間進(jìn)行選擇
內(nèi)置鎖仍具備的優(yōu)勢(shì):
- 為開發(fā)人員熟悉,并且簡(jiǎn)潔緊湊拳昌,還廣為使用袭异。
- ReentrantLock的危險(xiǎn)性高,僅當(dāng)內(nèi)置鎖不能滿足需求時(shí)炬藤,才會(huì)使用ReentrantLock
- 在java5.0中御铃,在線程轉(zhuǎn)儲(chǔ)中能給出在哪些調(diào)用幀中獲得了哪些鎖碴里,并能夠檢測(cè)和識(shí)別發(fā)生死鎖的線程。ReentrantLock的非塊結(jié)構(gòu)性仍意味著畅买,獲取鎖的操作不能和特定的幀關(guān)聯(lián)起來并闲。
- 未來更可能提升的是synchronized的性能
4. 讀-寫鎖
一個(gè)資源可以被多個(gè)讀操作訪問,或者被一個(gè)寫操作訪問谷羞,二者不能同時(shí)進(jìn)行帝火。
public interface ReadWriteLock{
Lock readLock();
Lock writeLock();
}
要讀取被ReadWriteLock保護(hù)的數(shù)據(jù),首先必須獲得讀取鎖湃缎,當(dāng)需要修改由ReadWriteLock保護(hù)的數(shù)據(jù)時(shí)犀填,必須先獲得寫入鎖∩のィ看上去兩個(gè)鎖是獨(dú)立的九巡,但是讀取鎖和寫入鎖只不過是讀寫鎖對(duì)象的不同視圖。
ReadWriteLock中一些可選實(shí)現(xiàn):
- **釋放優(yōu)先 : ** 當(dāng)一個(gè)寫入操作釋放寫入鎖時(shí)蹂季,并且隊(duì)列中同時(shí)存在讀線程和寫線程冕广,那么應(yīng)該優(yōu)先選擇讀線程,寫線程偿洁,還是最先發(fā)出請(qǐng)求的線程撒汉?
- **讀線程插隊(duì): ** 如果鎖是由讀線程持有,但有寫線程正在等待涕滋,那么新到達(dá)的讀線程能否立即獲得訪問權(quán)睬辐,還是應(yīng)該在寫線程后面等待?如果允許讀線程插入到寫線程前面宾肺,有助于提高并發(fā)性溯饵,但是有可能造成寫線程發(fā)生饑餓問題。
- **重入性 : ** 讀操作和寫操作是否為可重入的锨用?
- **降級(jí) : ** 如果一個(gè)線程持有寫入鎖丰刊,那么它能否在不釋放該鎖的情況下獲取讀取鎖?這樣可能會(huì)使得寫入鎖被降級(jí)為讀取鎖增拥,同時(shí)不允許其他寫線程修改被保護(hù)的資源藻三。
- **升級(jí) : ** 讀取鎖能否優(yōu)先于其他正在等待的讀線程和寫線程而升級(jí)為一個(gè)寫入鎖?
ReentrantReadWriteLock
ReentrantReadWriteLock為這兩種鎖都提供了可重入的加鎖定義跪者。并且在其構(gòu)造函數(shù)內(nèi)可選擇一個(gè)公平鎖或者非公平的鎖(默認(rèn))棵帽。
- 公平的鎖中:等待時(shí)間最長(zhǎng)的線程優(yōu)先獲得鎖。如果這個(gè)鎖被讀線程持有渣玲,而另一個(gè)線程請(qǐng)求寫入鎖逗概,那么其他讀線程都不能獲取讀取鎖,直到寫線程使用完并且釋放了寫入鎖忘衍。
- 在非公平的鎖中:線程獲得訪問許可的順序是不確定的逾苫。寫線程可以降級(jí)為讀線程卿城,但是讀線程不能升級(jí)為寫線程(導(dǎo)致死鎖)