歡迎來到《[併發王者課](https://juejin.cn/post/6967277362455150628)》,本文是該系列文章中的**第19篇**。

在上一篇文章中,我們介紹了阻塞佇列。如果你閱讀過它的原始碼,那麼你一定會注意到原始碼有兩個Condition型別的變數:`notEmpty`和`notFull`,在讀寫佇列時你也會注意到它們是如何被使用的。事實上,在使用JUC中的各種鎖時,Condition都很有用場,你很有必要了解它。所以,本文就為你介紹它的來龍去脈和用法。

在前面的系列文章中,我們多次提到過`synchronized`關鍵字,相信你已經對它的用法瞭如於心。在多執行緒協作時,有兩個另外的關鍵字經常和`synchronized`一同出現,它們相互配合,就是`wait`和`notify`,比如下面的這段程式碼:

```java
public class CountingSemaphore {
private int signals = 0;
public synchronized void take() {
this.signals++;
this.notify(); // 傳送通知
}
public synchronized void release() throws InterruptedException {
while (this.signals == 0)
this.wait(); // 釋放鎖,進入等待
This.signals--;
}
}
```

`synchronized`是Java的原生同步工具,`wait`和`notify`是它的原生搭檔。然而,在鉑金系列中,我們已經開始了Lock介面和它的一些實現,比如可重入鎖ReentrantLock等。相比於`synchronized`,JUC所封裝的這些鎖工具在功能上要豐富得多,也更加容易使用。所以,相應地配套自然也要跟上,於是Condition就應運而生。

比如在上文的阻塞佇列中,Condition就已經閃亮登場:

```java
public class LinkedBlockingQueue < E > extends AbstractQueue < E >
implements BlockingQueue < E > , java.io.Serializable {

...省略原始碼若干

// 定義Condition
// 注意,這裡定義兩個Condition物件,用於喚醒不同的執行緒
private final Condition notEmpty = takeLock.newCondition();
private final Condition notFull = putLock.newCondition();

public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
// 進入等待
notEmpty.await();
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}

private void signalNotEmpty() {
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
// 傳送喚醒訊號
notEmpty.signal();
} finally {
takeLock.unlock();
}
}
...省略原始碼若干

}
```

**從功能定位上說,作為Lock的配套工具,Condition是`wait`、`notify`和`notifyAll`增強版本,`wait`和`notify`有的能力它都有,`wait`和`notify`沒有的能力它也有**。

JUC中的Condition是以介面的形式出現,並定義了一些核心方法:

* `await()`:讓當前執行緒進入等待,直到收到訊號或者被中斷;

* `await(long time, TimeUnit unit)`:讓當前執行緒進入等待,直到收到訊號或者被中斷,或者到達指定的等待超時時間;
* `awaitNanos(long nanosTimeout)`:讓當前執行緒進入等待,直到收到訊號或者被中斷,或者到達指定的等待超時時間,只是在時間單位上和上一個方法有所區別;
* `awaitUninterruptibly()`:**讓當前執行緒進入等待,直到收到訊號。注意,這個方法對中斷是不敏感的**;
* `awaitUntil(Date deadline)`:**讓當前執行緒進入等待,直到收到訊號或者被中斷,或者到達截止時間**;
* `signal()`:隨機喚醒一個執行緒;
* `signalAll()`:喚醒所有等待的執行緒。

**從Condition的核心方法中可以看到,相較於原生的通知與等待,它的能力明顯增強了很多,比如`awaitUninterruptibly()`和`awaitUntil()`。另外,Condition竟然是可以喚醒指定執行緒的,這就很有意思**。

作為介面,我們並不需要手動實現Condition,JUC已經提供了相關的實現,你可以在ReentrantLock中直接使用它。相關的類、介面之間的關係如下所示:

![](https://writting.oss-cn-beijing.aliyuncs.com/2021/06/27/16247728628301.jpg)

## 小結

以上就是關於Condition的全部內容。Condition並不複雜,它是JUC中Lock的配套,在理解時要結合原生的`wait`和`notify`去理解。關於Condition與它們之間的詳細區別,已經都在下面的表格裡:

|對比項|Object's Monitor methods| Condition
|---|---|---|
|**前置條件**|獲取物件的鎖|呼叫Lock獲取鎖,呼叫lock.newCondition()獲取Condition物件|
|**呼叫方式**|直接呼叫,如object.wait()|直接呼叫,如condition.await()|
|**等待佇列個數**|一個|**多個**|
|**當前執行緒釋放鎖並進入等待狀態**|︎|︎|
|**當前執行緒釋放鎖並進入等待狀態,在等待時不響應中斷**|✘|**︎**|
|**當前執行緒釋放鎖並進入超時等待**|︎|︎|
|**當前執行緒釋放鎖並進入等待到未來某個時刻**|✘|**︎**|
|**喚醒等待佇列中的某一個執行緒**|︎|︎|
|**喚醒等待佇列中的全部執行緒**|︎|︎|

理解表格中的各項差異,不要死記硬背,而是要基於Condition介面中定義的方法,從關鍵處理解它們的不同。

正文到此結束,恭喜你又上了一顆星

**夫子的試煉**

* 編寫程式碼使用Condition喚醒指定執行緒。

**延伸閱讀與參考資料**

* 小結表格中的內容由網路圖片提取,未能找到原始出處,知道的請評論告知,感謝!

* [《併發王者課》大綱與更新進度總覽](https://juejin.cn/post/6967277362455150628)

**最新修訂及更好閱讀體驗**

* [閱讀掘金原文](https://juejin.cn/post/6976063081751248909/)

**關於作者**

關注公眾號【**技術八點半**】,及時獲取文章更新。傳遞有品質的技術文章,記錄平凡人的成長故事,偶爾也聊聊生活和理想。早晨8:30推送作者品質原創,晚上20:30推送行業深度好文。

如果本文對你有幫助,歡迎**點贊**、**關注**、**監督**,我們一起**從青銅到王者**。