wait(),notify() 與 await(), signal(), signalAll() 的區別
阿新 • • 發佈:2019-01-27
參考文章
wait() 和 notify() 的使用方式
wait()
與notify()
需要搭配synchronized
關鍵字使用, 示例如下
// 執行緒 A 的程式碼
synchronized(obj_A)
{
while(!condition){
obj_A.wait();
}
// do something
}
// 執行緒 B 的程式碼
synchronized(obj_A)
{
if(!condition){
// do something ...
condition = true;
obj_A.notify();
}
}
- 為什麼
wait()
,notify()
需要搭配synchronized
關鍵字使用
- wait(), notify() 操作的目的是基於某種條件, 協調多個執行緒間的執行狀態, 由於涉及到多個執行緒間基於共享變數的相互通訊, 必然需要引入某種同步機制, 以確保wait(), notify() 操作線上程層面的原子性
await() 和 signal() 的使用方式
- wait() 和 notify() 方法是 Object 的方法, 而 await() 和 signal() 方法是介面 Condition 的方法, 官方文件對於 await() 和 signal() 有如下的說明。
Condition factors out the Object monitor methods (wait, notify and notifyAll) into distinct objects to give the effect of having multiple wait-sets per object, by combining them with the use of arbitrary Lock implementations.
- 翻譯一下就是, Condition 這個介面把 Object 的
wait(), notify(), notifyAll()
分解到了不同的物件中, 搭配上任意一種 Lock 的使用, 使得一個物件可以擁有多個等待集 這裡有一個術語是
waitset
, 具體含義是- 每個物件都有一個等待集, 該等待集是一個執行緒的集合。
wait(), notify(), notifyAll()
方法的呼叫會對等待集中的執行緒進行移入或移除操作
- 每個物件都有一個等待集, 該等待集是一個執行緒的集合。
所以, await() 和 signall 的新增, 實際上是為我們提供了一種方便的基於同一個鎖, 實現多個條件的 wait() 和 notify() 操作, 以一個有界緩衝區的生產消費操作為例(該例子實際上是
ArrayBlockingQueue
的簡化版 , 感興趣的讀者可以直接閱讀原始碼)
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
- 上面的例子中, 在一個 lock 控制的臨界區中, 出現了兩種條件(notFull, notEmpty)的操作。
- 下面假設我們要通過 wait() 和 notify() 粗暴地進行替換
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
synchronized(lock)
{
while (count == items.length)
lock.wait();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
lock.notify();
}
}
public Object take() throws InterruptedException {
synchronized(lock){
while (count == 0)
lock.wait();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
lock.notify();
return x;
}
}
}
- 顯然, 上述的程式碼僅僅在消費者和生產者分別只有一個時可以工作, 在有多個生產者和消費者時, 生產者A 可能會喚醒生產者 B 造成錯誤的結果, 為了避免這一錯誤。 則需要多個新增更多的物件, 使用巢狀的 synchronized 程式碼塊。
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
synchronized (lock) {
synchronized (notFull) {
while (count == items.length)
notFull.wait();
}
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
synchronized (notEmpty) {
notEmpty.notify();
}
}
}
public Object take() throws InterruptedException {
synchronized (lock) {
synchronized (notEmpty)
{
while (count == 0)
notEmpty.wait();
}
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
synchronized (notFull)
{
notFull.notify();
}
return x;
}
}
}
- 顯然, 程式碼的易用性和可讀性都不如 Condition 方法的 await() 和 signal() 。
總結
await(), signal(),signalAll() 的功用和 wait(), notify(), notifyAll() 基本相同, 區別是, 基於 Condition 的 await(), signal(), signalAll() 使得我們可以在同一個鎖的程式碼塊內, 優雅地實現基於多個條件的執行緒間掛起與喚醒操作。