Lock接口提供了方法Condition newCondition();用於獲取對應鎖的條件,可以在這個條件對象上調用監視器方法
可以理解為,原本借助於synchronized關鍵字以及鎖對象,配備了一個監視器
而顯式鎖Lock與Condition則針對於一個鎖對象,提供了多個監視器
盡管是提供了多個監視器,但是需要記住,是Lock接口提供方法才能夠獲取到條件對象,所以這些條件對象仍舊是綁定到某一把鎖上的
我相信,只要理解了監視器的概念,對於Condition理解起來是不會存在任何難度的,因為本身就是另外一種實現方式
從下圖可以直觀的感受到Condition是作為Object監視器方法的另外實現
wait在這邊叫做await
notify在這邊叫做signal
等待
await
在監視器上等待
void await() throws InterruptedException; ,看得出來,此方法是支持中斷的
除非發生以下事件,否則將會持續等待
- 其他某個線程調用此 Condition 的 signal() 方法,並且碰巧喚醒的是該線程
- 其他某個線程調用此 Condition 的 signalAll() 方法;
- 其他某個線程中斷當前線程
- “虛假喚醒”
awaitUninterruptibly
不支持中斷的await方法,void awaitUninterruptibly();
從await()的介紹中看得出來,除非發生提到的四種情況,否則將會是等待狀態
而void awaitUninterruptibly();則是await()的不可中斷版本,也就是只會有三種情況會跳出等待
- 其他某個線程調用此 Condition 的 signal() 方法,並且碰巧喚醒的是該線程
- 其他某個線程調用此 Condition 的 signalAll() 方法;
- 其他某個線程中斷當前線程
- “虛假喚醒”
awaitNanos
long awaitNanos(long nanosTimeout) throws InterruptedException;
支持設置超時的等待,參數為等待的納秒的long型數值
他在基於await的前提下,新增加了超時跳出,否則將會一直等待,他的跳出條件如下
- 其他某個線程調用此 Condition 的 signal() 方法,並且碰巧喚醒的是該線程
- 其他某個線程調用此 Condition 的 signalAll() 方法;
- 其他某個線程中斷當前線程
- “虛假喚醒”
- 已超過指定的等待時間(新增)
返回值
需要注意的是此方法的返回值,是一個long
我們設置了一個超時時間,那么到底用了多少秒,還剩下多少秒?這個返回值就是這意思:
(nanosTimeout - 實際花費時間)的估算值,如果小於等於0,表示沒有剩余時長;如果大於0,可以用來確定如果等待返回了是否還需要繼續等待?
比如,你在等朋友A,A說你等我一小時,於是你去睡覺了,結果你睡了半小時就被叫醒了,那么跟你本來要等的時間還差半小時
那還要不要繼續等了?還是一定要等到總共一小時呢?
典型用法(來自API):
boolean aMethod(long timeout, TimeUnit unit) { long nanos = unit.toNanos(timeout); lock.lock(); try { while (!conditionBeingWaitedFor()) { if (nanos <= 0L) return false; nanos = theCondition.awaitNanos(nanos); } // ... } finally { lock.unlock(); } }
上面的方法中,如果條件仍舊不滿足,但是等待結束了(也就是等待了足夠多的時間了),直接返回false;否則將會繼續執行,直到等到最后一刻
ps:這種代碼風格也就JDK常寫,否者這種if形式,估計要被項目經理罵
await(long time, TimeUnit unit)
boolean await(long time, TimeUnit unit) throws InterruptedException;
此方法也是設置超時時長的等待方法,第一個參數為時長個數,第二個參數為第一個參數的單位
他相當於awaitNanos方法的封裝(此處是邏輯上看起來,而不是說就是這個方法的封裝)
awaitNanos(unit.toNanos(time)) > 0
所以返回類型為boolean,顯然true表示沒有等待足夠的時間;,false 表示等待了足夠時間,也就是等待超時
awaitUntil
boolean awaitUntil(Date deadline) throws InterruptedException;
awaitUntil類似於await(long time, TimeUnit unit),只不過不是設置超時時長,而是設置截止日期
邏輯上可以把他們理解為一回事,如果沒有等待足夠時長,那么返回true;如果等待超時那么返回false
常用的邏輯(來自API)
boolean aMethod(Date deadline) { boolean stillWaiting = true; lock.lock(); try { while (!conditionBeingWaitedFor()) { if (!stillWaiting) return false; stillWaiting = theCondition.awaitUntil(deadline); } // ... } finally { lock.unlock(); } }
上面的代碼中,如果等待了足夠的時長(等待超時),那么就會返回false;如果還有剩余時間,繼續等待
普通的await()方法和awaitUninterruptibly都是直白的等待,一個支持中斷,一個不支持中斷
有超時設置的三個方法:
awaitNanos、await(long time, TimeUnit unit)、awaitUntil
都是在await()的基礎上對超時時長或者截止日期的設置使用
不過這幾個方法會返回剩余的超時時長或者使用boolean指示是否還有剩余
所以如果條件不滿足,如果還沒等夠時間,可以控制繼續等待或者退出
而對於Object提供的wait方法,就不能做到這么靈活的控制,要么就條件不滿足繼續等待,要么醒來后繼續做別的事情,沒辦法相對准確的控制“必須要等待一定的時長”,因為如果wait(一小時),5分鍾后,被喚醒了,這個用掉了的五分鍾(或者說還剩余55分鍾,是不知道的)
喚醒
關於喚醒有與Object提供的類似的兩個方法,他們的邏輯含義也是一樣的,喚醒一個或者喚醒所有,概念上並沒有太多需要注意的事情
void signal();
void signalAll();
總結
Condition本身就是Object中監視器概念的另外實現,所以監視器方法調用,也需要持有鎖,也就是說:
調用Condition的await()和signal()等方法,都必須在lock保護之內,就是說必須在lock.lock()和lock.unlock之間才可以使用
await系列方法相對於Object提供了更加靈活的使用形式,signal和signalAll的邏輯含義可以認為完全一致
另外需要注意await方法與Object中的wait一樣,都會釋放當前持有的鎖,所以醒來之后,會需要重新獲取鎖