該文章屬於《Java併發編程》系列文章,若是想了解更多,請點擊《Java併發編程之總目錄》編程
在上篇文章《Java併發編程之鎖機制之引導篇》及相關實現類,咱們大體瞭解了Lock接口(以及相關實現類)在併發編程重要做用。接下來咱們就來具體瞭解Lock接口中聲明的方法以及使用優點。bash
Lock 接口實現類提供了比使用 synchronized 方法和語句可得到的更普遍的鎖定操做。此實現容許更靈活的結構,能夠具備差異很大的屬性,能夠支持多個相關的 Condition
(Condition實現類ConditonObject來實現線程的通知/與喚醒機制,關於Condition後期會進行介紹)對象。多線程
鎖是用於控制多線程訪問共享資源的工具。一般,鎖提供對共享資源的獨佔訪問:一次只有一個線程能夠獲取鎖,對共享資源的全部訪問都須要首先獲取鎖。可是,一些鎖能夠容許同時訪問共享資源,例如ReadWriteLock
。併發
雖然使用關鍵字synchronized修飾的方法或代碼塊,會使得在監視器模式(ObjectMonitor)下編程變得很是容易(經過synchronized塊或者方法所提供的隱式獲取釋放鎖的便捷性)。雖然這種方式簡化了鎖的管理,可是某些狀況下,仍是建議採用Lock接口(及其相關子類)提供的顯示的鎖的獲取和釋放。例如,針對一個場景,手把手進行鎖獲取和釋放,先得到鎖A,而後再獲取鎖B,當鎖B得到後,釋放鎖A同時獲取鎖C,當鎖C得到後,再釋放B同時獲取鎖D,以此類推。這種場景下, synchronized關鍵字就不那麼容易實現了,而Lock接口的實現類容許鎖在不一樣的做用範圍內獲取和釋放
,並容許以任何順序獲取和釋放多個鎖。ide
關於Lock接口中涉及到的方法具體以下:(建議直接在PC端查看,手機上有可能看的不是很清楚) 工具
其中Lock的使用方式也很簡單,具體代碼以下所示:post
Lock lock = ....;具體實現類
lock.lock();
try {
} finally {
lock.unlock();//建議在finally中釋放鎖
}
複製代碼
當鎖定和解鎖發生在不一樣的範圍時,必定要注意確保在持有鎖時執行的全部代碼都受到try-finally或try-catch的保護,以確保在必要時釋放鎖。不要將獲取鎖的過程寫在try塊中
,由於若是在獲取鎖(自定義鎖的實現)時發生了異常,異常拋出的同時,也會致使鎖無端釋放(由於一旦發生異常,就會走finally語句,若是這個異常(多是用戶自定義異常,用戶能夠本身處理)須要線程1來處理,可是接着執行了lock.unlock()語句致使了鎖的釋放。那麼其餘線程就能夠操做共享資源。有可能破壞程序的執行結果)。ui
爲了使用Lock接口實現相關鎖功能時,會涉及如下類和接口,這裏仍是把上篇文章提到的UML圖展現出來:spa
上圖中,線程
ReentrantLock(重入鎖)
、WriteLock、ReadLock都是Lock的實現類。Segment爲ReentrantLock的子類(在後續文章,ConcurrentHashMap的講解中咱們會說起)。
ReentrantReadWriteLock (讀寫鎖)
的實現使用了WriteLock與ReadLock類。AbstractQueuedSynchronizer
與AbstractQueuedLongSynchronizer
都爲AbstractOwnableSynchronizer
的子類,該兩個類中都維護了一個同步隊列,用於線程的併發執行。在該兩個類中擁有名爲ConditionObject(爲Conditon的實現類)
的內部類,只是其內部實現不一樣。在ConditionObject內部維護了一個等待隊列,用於控制線程的等待與喚醒。在瞭解了Lock相關實現類實現鎖機制後,這裏給實現該鎖機制的大體代碼結構(根據不一樣需求,部分方法實現可能不同,這裏只是一個參考,並非樣本代碼)。具體代碼以下所示:
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接口下實現的鎖機制中,AQS(這裏咱們將AbstractQueuedSynchronizer 或AbstractQueuedLongSynchronizer統稱爲AQS)
是實現鎖的關鍵,整個鎖的實現是在Lock類的實現類中聚合AQS來實現的,從代碼層面上來講,Lock接口(及其實現類)是面向使用者的,它定義了使用者與鎖交互的接口(好比能夠容許兩個線程並行訪問),隱藏了實現細節。AQS與Condition纔是真正的實現者,它簡化了鎖的實現方式,屏蔽了同步狀態管理、線程的排隊、等待與喚醒等底層操做。