一、爲什麼需要wait/notify?
我們都知道wati/notify用於線程間的同步。
假設有一把鎖lock,此時線程t1來持有這把鎖,但是由於其不滿足業務條件暫時不能繼續執行,如果t2此時來獲取鎖,發現鎖仍被t1佔有,則會發生阻塞,造成嚴重的效率問題,甚至出現死鎖。
通過wait/notify,可是是t1先進入WAITING狀態,等待t2的喚醒,被喚醒後再去執行對應的操作,這樣就避免了t2線程的阻塞,且t1、t2之間的競爭也沒有了,大大提升執行效率。
二、wait/notify原理
前面我們講過Monitor(管程)的結構,其中有一塊叫做waitSet的區域,裏面存放的是狀態爲WAITING狀態的線程。如下圖虛線框中的內容:
涉及到Monitor,相信大家能夠知道,只有在重量級鎖中,纔會有wait/notify。
下面按照上圖簡單分析下其流程:
- 假設線程thread1持有了重量級鎖,即Owner,但是此時沒有準備好,所以調用了wait方法。此時其狀態就會變成WAITING,進入到Monitor的WaitSet當中。
- EntryList和WaitSet當中的線程,都是不佔用CPU時間片的。
- EntryList當中的線程,會等待Owner執行完成後被喚醒。
- WaitSet當中的線程,會等待Owner執行notify或notifyAll方法進行喚醒,但是並不是直接獲取到鎖,而是被添加到EntryList當中進行非公平的競爭。
三、wait/notify使用
3.1 常用方法
- obj.wait() 讓當前持有鎖的Owner進入Monitor的WaitSet進行等待。
- obj.wait(long timeout) 讓進入 object 監視器的線程到 waitSet 等待,可制定等待超時時間。
- obj.notify() Owner從正在 waitSet 等待的線程中挑一個喚醒。
- obj.notifyAll() Owner讓正在 waitSet 等待的線程全部喚醒。
以上方法都是Object類的方法,必須要獲取該對象鎖,才能調用上述方法。
通過下面的例子,演示用法,媽媽正在做飯,爸爸和小A等着喫飯,爸爸發現飯沒有做好,要去睡一會;小A發現飯沒有做好,要去玩一會兒。媽媽做好飯了,叫小A和爸爸喫飯。飯後,小A去寫作業了,爸爸去釣魚了:
/**
* 吃了嗎
*/
static boolean EATEN_YET = false;
static Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock) {
while (true) {
if (EATEN_YET) {
System.out.println(Thread.currentThread().getName() + ":我要開始寫作業了!!");
break;
} else {
try {
System.out.println(Thread.currentThread().getName() + ":喫飯前我先玩一會兒!!");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}, "小A").start();
new Thread(() -> {
synchronized (lock) {
while (true) {
if (EATEN_YET) {
System.out.println(Thread.currentThread().getName() + ":我要去釣魚了!!");
break;
} else {
try {
System.out.println(Thread.currentThread().getName() + ":喫飯前我先睡一會兒!!");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}, "爸爸").start();
new Thread(() -> {
synchronized (lock) {
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":開飯了!!");
EATEN_YET = true;
lock.notifyAll();
}
}, "媽媽").start();
}
結果:
小A:喫飯前我先玩一會兒!!
爸爸:喫飯前我先睡一會兒!!
媽媽:開飯了!!
爸爸:我要去釣魚了!!
小A:我要開始寫作業了!!