在jdk1.5之后伸眶,并發(fā)包中新增了Lock接口(以及相關(guān)實(shí)現(xiàn)類)用來實(shí)現(xiàn)鎖功能,Lock接口提供了與synchronized關(guān)鍵字類似的同步功能敷扫,但需要在使用時手動獲取鎖和釋放鎖哀蘑。雖然Lock接口沒有synchronized關(guān)鍵字自動獲取和釋放鎖那么便捷,但Lock接口卻具有了鎖的可操作性葵第,可中斷獲取以及超時獲取鎖等多種非常實(shí)用的同步特性绘迁,除此之外Lock接口還有兩個非常強(qiáng)大的實(shí)現(xiàn)類重入鎖和讀寫鎖。
Lock接口的使用
Lock lock = new ReentrantLock();
lock.lock();
try{
//可能會出現(xiàn)線程安全的操作
}finally{
//一定在finally中釋放鎖
//也不能把獲取鎖在try中進(jìn)行卒密,因?yàn)橛锌赡茉讷@取鎖的時候拋出異常
lock.ublock();
}
---Lock接口與synchronized關(guān)鍵字的區(qū)別:
? Lock接口可以嘗試非阻塞地獲取鎖 當(dāng)前線程嘗試獲取鎖缀台。如果這一時刻鎖沒有被其他線程獲取到,則成功獲取并持有鎖哮奇。
*Lock接口能被中斷地獲取鎖 與synchronized不同膛腐,獲取到鎖的線程能夠響應(yīng)中斷,當(dāng)獲取到的鎖的線程被中斷時鼎俘,中斷異常將會被拋出哲身,同時鎖會被釋放。
? Lock接口在指定的截止時間之前獲取鎖而芥,如果截止時間到了依舊無法獲取鎖律罢,則返回。
Lock接口的API
? void lock() 獲取鎖,調(diào)用該方法當(dāng)前線程將會獲取鎖棍丐,當(dāng)鎖獲取后误辑,該方法將返回。
? void lockInterruptibly() throws InterruptedException 可中斷獲取鎖歌逢,與lock()方法不同之處在于該方法會響應(yīng)中斷巾钉,即在鎖的獲取過程中可以中斷當(dāng)前線程
? boolean tryLock() 嘗試非阻塞的獲取鎖,調(diào)用該方法立即返回秘案,true表示獲取到鎖
? boolean tryLock(long time,TimeUnit unit) throws InterruptedException 超時獲取鎖砰苍,以下情況會返回:時間內(nèi)獲取到了鎖潦匈,時間內(nèi)被中斷,時間到了沒有獲取到鎖赚导。
? void unlock() 釋放鎖
了解Lock接口最常用的實(shí)現(xiàn)ReentrantLock重入鎖
---ReentrantLock是Lock接口一種常見的實(shí)現(xiàn)茬缩,它是支持重進(jìn)入的鎖即表示該鎖能夠支持一個線程對資源的重復(fù)加鎖。該鎖還支持獲取鎖時的公平與非公平的選擇吼旧。
關(guān)于鎖的重進(jìn)入凰锡,其實(shí)synchronized關(guān)鍵字也支持。如前所述圈暗,synchronized關(guān)鍵字也是隱式的支持重進(jìn)入而對于ReentrantLock而言掂为,對于已經(jīng)獲取到鎖的線程,再次調(diào)用lock()方法時依然可以獲取鎖而不被阻塞员串。
理解了鎖的重進(jìn)入勇哗,現(xiàn)在解釋剛剛提到的公平獲取鎖與非公平獲取鎖。如果在絕對時間上寸齐,先對于鎖進(jìn)行獲取的請求一定先被滿足欲诺,那么這個鎖就是公平的,反之就是非公平的渺鹦。公平的獲取鎖也就是等待時間最久的線程優(yōu)先獲取到鎖瞧栗。ReentrantLock的構(gòu)造函數(shù)來控制是否為公平鎖。
---在第一次了解到公平鎖于非公平鎖的時候海铆,第一反應(yīng)是公平鎖的效率高,應(yīng)該使用公平鎖挣惰。但實(shí)際的情況是卧斟,非公平的鎖的效率遠(yuǎn)遠(yuǎn)大于公平鎖。
---了解Lock接口的實(shí)現(xiàn)類ReentrantReadWriteLock讀寫鎖
---前面提到的ReentrantLock是排他鎖憎茂,該鎖在同一時刻只允許一個線程來訪問珍语,而讀寫鎖在同一時刻允許可以有多個線程來訪問,但在寫線程訪問時竖幔,所有的讀線程和其他寫線程被阻塞板乙。讀寫鎖維護(hù)了一對鎖,一個讀鎖和一個寫鎖拳氢,通過讀寫鎖分離募逞,使得并發(fā)性相比一般的排他鎖有了很大的提升。
---讀寫鎖除了使用在寫操作happends-before與讀操作以及并發(fā)性的提升之外馋评,讀寫鎖也能夠簡化讀寫交互場景的編程方式放接。假設(shè)在程序中定義一個共享的用作緩存數(shù)據(jù)結(jié)構(gòu),它的大部分時間提供讀服務(wù)(查詢留特,搜索等)而寫操作較少纠脾,但寫操作之后需要立即對后續(xù)的讀操作可見玛瘸。在沒有讀寫鎖之前,實(shí)現(xiàn)這個功能需要使用等待通知機(jī)制苟蹈。無論使用那種方式糊渊,目的都是為了寫操作立即可見于讀操作而避免臟讀。但使用讀寫鎖卻比等待通知簡單明了多了慧脱。
---一般情況下渺绒,讀寫鎖性能優(yōu)于排他鎖。它能提供更好的并發(fā)性和吞吐量磷瘤。
ReentrantReadWriteLock讀寫鎖的幾個特性:
? 公平選擇性
? 重進(jìn)入
? 鎖降級
讀寫鎖的示例:緩存
public class Cache{
static Map<String,Object> map = new HashMap<String,Object>();
static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
static Lock rLock = rwl.readLock();
static Lock wLock = rwl.writeLock();
//獲取一個key對應(yīng)的value
public static final Object get(String key){
r.lock();
try{
return map.get(key);
}finally{
r.unlock();
}
}
//設(shè)置key對應(yīng)的value并返回舊的value
public static fianl Object put(String key,Object value){
w.lock();
try{
return map.put(key,value);
}final{
w.unlock();
}
}
//清空緩存
public static fianl void clear(){
w.lock();
try{
map.clear();
} finally{
w.unlock();
}
}
}
---上述緩存示例中芒篷,我們使用了一個非線程安全的HashMap作為緩存的時候然后使用讀寫鎖來保證線程安全。Cache使用讀寫鎖提升讀操作的并發(fā)性采缚,也保證每次寫操作對讀操作的及時可見性针炉,同時簡化了編程方式。
讀寫鎖的鎖降級
---鎖降級是指寫鎖降級成為讀鎖扳抽。如果當(dāng)前線程持有寫鎖篡帕,然后將其釋放再獲取讀鎖的過程不能稱為鎖降級。鎖降級指的在持有寫鎖的時候再獲取讀鎖,獲取到讀鎖后釋放之前寫鎖的過程稱為鎖釋放贸呢。
---鎖降級在某些情況下是非常必要的镰烧,主要是為了保證數(shù)據(jù)的可見性。如果當(dāng)前線程不獲取讀鎖而直接釋放寫鎖楞陷,假設(shè)此時另外一個線程獲取了寫鎖并修改了數(shù)據(jù)怔鳖。那么當(dāng)前線程無法感知該線程的數(shù)據(jù)更新。