該文章屬于《Java并發(fā)編程》系列文章缭贡,如果想了解更多窗价,請點(diǎn)擊《Java并發(fā)編程之總目錄》
前言
在上篇文章《Java并發(fā)編程之鎖機(jī)制之引導(dǎo)篇》中灿椅,我們大致了解了Lock接口(以及相關(guān)實(shí)現(xiàn)類)在并發(fā)編程重要作用殊者。接下來我們就來具體了解Lock接口中聲明的方法以及使用優(yōu)勢恨憎。
Lock簡介
Lock 接口實(shí)現(xiàn)類提供了比使用 synchronized 方法和語句可獲得的更廣泛的鎖定操作蕊退。此實(shí)現(xiàn)允許更靈活的結(jié)構(gòu),可以具有差別很大的屬性憔恳,可以支持多個相關(guān)的 Condition
(Condition實(shí)現(xiàn)類ConditonObject來實(shí)現(xiàn)線程的通知/與喚醒機(jī)制咕痛,關(guān)于Condition后期會進(jìn)行介紹)對象。
鎖是用于控制多線程訪問共享資源的工具喇嘱。通常茉贡,鎖提供對共享資源的獨(dú)占訪問:一次只有一個線程可以獲取鎖,對共享資源的所有訪問都需要首先獲取鎖者铜。但是腔丧,一些鎖可以允許同時(shí)訪問共享資源,例如ReadWriteLock
作烟。
雖然使用關(guān)鍵字synchronized修飾的方法或代碼塊愉粤,會使得在監(jiān)視器模式(ObjectMonitor)下編程變得非常容易(通過synchronized塊或者方法所提供的隱式獲取釋放鎖的便捷性)。雖然這種方式簡化了鎖的管理拿撩,但是某些情況下衣厘,還是建議采用Lock接口(及其相關(guān)子類)提供的顯示的鎖的獲取和釋放。例如压恒,針對一個場景影暴,手把手進(jìn)行鎖獲取和釋放,先獲得鎖A探赫,然后再獲取鎖B型宙,當(dāng)鎖B獲得后,釋放鎖A同時(shí)獲取鎖C伦吠,當(dāng)鎖C獲得后妆兑,再釋放B同時(shí)獲取鎖D,以此類推毛仪。這種場景下搁嗓,
synchronized關(guān)鍵字就不那么容易實(shí)現(xiàn)了,而Lock接口的實(shí)現(xiàn)類允許鎖在不同的作用范圍內(nèi)獲取和釋放
箱靴,并允許以任何順序獲取和釋放多個鎖腺逛。
Lock接口中的方法
關(guān)于Lock接口中涉及到的方法具體如下:(建議直接在PC端查看,手機(jī)上有可能看的不是很清楚)
從上表中刨晴,我們就可以得出使用Lock接口實(shí)現(xiàn)的鎖機(jī)制與使用傳統(tǒng)的synchronized的區(qū)別
- 嘗試非阻塞地獲取鎖:當(dāng)線程嘗試獲取鎖屉来,如果這一時(shí)刻鎖沒有被其他線程獲取到路翻,則成功獲取并持有鎖。
- 能被中斷的獲取鎖:與synchronized不同茄靠,獲取到鎖的線程能夠響應(yīng)中斷茂契,當(dāng)獲取到鎖的線程被中斷時(shí),中斷異常會被拋出慨绳,同時(shí)鎖也會被釋放掉冶。
- 超時(shí)獲取鎖:在指定的截止時(shí)間之前獲取鎖,如果截止時(shí)間到了任然無法獲取到鎖脐雪,則返回厌小。
Lock簡單使用與注意事項(xiàng)
其中Lock的使用方式也很簡單,具體代碼如下所示:
Lock lock = ....;具體實(shí)現(xiàn)類
lock.lock();
try {
} finally {
lock.unlock();//建議在finally中釋放鎖
}
當(dāng)鎖定和解鎖發(fā)生在不同的范圍時(shí)战秋,一定要注意確保在持有鎖時(shí)執(zhí)行的所有代碼都受到try-finally或try-catch的保護(hù)璧亚,以確保在必要時(shí)釋放鎖。不要將獲取鎖的過程寫在try塊中
脂信,因?yàn)槿绻讷@取鎖(自定義鎖的實(shí)現(xiàn))時(shí)發(fā)生了異常癣蟋,異常拋出的同時(shí),也會導(dǎo)致鎖無故釋放(因?yàn)橐坏┌l(fā)生異常狰闪,就會走finally語句疯搅,如果這個異常(可能是用戶自定義異常,用戶可以自己處理)需要線程1來處理埋泵,但是接著執(zhí)行了lock.unlock()語句導(dǎo)致了鎖的釋放幔欧。那么其他線程就可以操作共享資源。有可能破壞程序的執(zhí)行結(jié)果)丽声。
Lock相關(guān)實(shí)現(xiàn)類實(shí)現(xiàn)鎖機(jī)制
為了使用Lock接口實(shí)現(xiàn)相關(guān)鎖功能時(shí)礁蔗,會涉及以下類和接口,這里還是把上篇文章提到的UML圖展示出來:
上圖中恒序,
- 綠色部分為:其中
ReentrantLock(重入鎖)
瘦麸、WriteLock谁撼、ReadLock都是Lock的實(shí)現(xiàn)類歧胁。Segment為ReentrantLock的子類(在后續(xù)文章,ConcurrentHashMap的講解中我們會提及)厉碟。
ReentrantReadWriteLock (讀寫鎖)
的實(shí)現(xiàn)使用了WriteLock與ReadLock類喊巍。 - 紫色部分為:其中
AbstractQueuedSynchronizer
與AbstractQueuedLongSynchronizer
都為AbstractOwnableSynchronizer
的子類,該兩個類中都維護(hù)了一個同步隊(duì)列箍鼓,用于線程的并發(fā)執(zhí)行崭参。在該兩個類中擁有名為ConditionObject(為Conditon的實(shí)現(xiàn)類)
的內(nèi)部類,只是其內(nèi)部實(shí)現(xiàn)不同款咖。在ConditionObject內(nèi)部維護(hù)了一個等待隊(duì)列何暮,用于控制線程的等待與喚醒奄喂。
基本代碼結(jié)構(gòu)
在了解了Lock相關(guān)實(shí)現(xiàn)類實(shí)現(xiàn)鎖機(jī)制后,這里給實(shí)現(xiàn)該鎖機(jī)制的大致代碼結(jié)構(gòu)(根據(jù)不同需求海洼,部分方法實(shí)現(xiàn)可能不一樣跨新,這里只是一個參考,并不是樣本代碼)坏逢。具體代碼如下所示:
class LockImpl implements Lock {
private final sync mSync = new sync();
@Override
public void lock() {
mSync.acquire(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
mSync.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return mSync.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return mSync.tryAcquireNanos(1, unit.toNanos(time));
}
@Override
public void unlock() {
mSync.release(1);
}
@Override
public Condition newCondition() {
return mSync.newCondition();
}
//這里也可以繼承AbstractQueuedLongSynchronizer
private static class sync extends AbstractQueuedSynchronizer {
@Override
protected boolean isHeldExclusively() {...}
@Override
protected boolean tryAcquire(int arg) {...}
@Override
protected boolean tryRelease(int arg) {...}
@Override
protected int tryAcquireShared(int arg) {...}
@Override
protected boolean tryReleaseShared(int arg) {...}
final ConditionObject newCondition() {...}
}
}
從代碼中我們可以看出域帐,在整個Lock接口下實(shí)現(xiàn)的鎖機(jī)制中,AQS(這里我們將AbstractQueuedSynchronizer 或AbstractQueuedLongSynchronizer統(tǒng)稱為AQS)
是實(shí)現(xiàn)鎖的關(guān)鍵是整,整個鎖的實(shí)現(xiàn)是在Lock類的實(shí)現(xiàn)類中聚合AQS來實(shí)現(xiàn)的肖揣,從代碼層面上來說,Lock接口(及其實(shí)現(xiàn)類)是面向使用者的浮入,它定義了使用者與鎖交互的接口(比如可以允許兩個線程并行訪問)龙优,隱藏了實(shí)現(xiàn)細(xì)節(jié)。AQS與Condition才是真正的實(shí)現(xiàn)者事秀,它簡化了鎖的實(shí)現(xiàn)方式陋率,屏蔽了同步狀態(tài)管理、線程的排隊(duì)秽晚、等待與喚醒等底層操作瓦糟。
總結(jié)
- Lock接口(及其實(shí)現(xiàn)類)相比synchronized有如下優(yōu)點(diǎn):
- 鎖的釋放與獲取不在是隱式的,允許鎖在不同的作用范圍內(nèi)獲取和釋放`赴蝇,并允許以任何順序獲取和釋放多個鎖菩浙。
- 能被中斷的獲取鎖,獲取到鎖的線程能夠響應(yīng)中斷句伶,當(dāng)獲取到鎖的線程被中斷時(shí)劲蜻,中斷異常會被拋出,同時(shí)鎖也會被釋放
- 超時(shí)獲取鎖:在指定的截止時(shí)間之前獲取鎖考余,如果截止時(shí)間到了任然無法獲取到鎖先嬉,則返回。
- 在使用Lock的時(shí)候注意楚堤,一定要確保必要時(shí)釋放鎖
- 在整個Lock接口下實(shí)現(xiàn)的鎖機(jī)制中,AQS(上文進(jìn)行了統(tǒng)稱)與Condition才是真正的實(shí)現(xiàn)者疫蔓。