wait和notify
阿新 • • 發佈:2020-12-29
wait notify 原理
- Owner 執行緒發現條件不滿足,呼叫 wait 方法,即可進入 WaitSet 變為 WAITING 狀態
- BLOCKED 和 WAITING 的執行緒都處於阻塞狀態,不佔用 CPU 時間片
- BLOCKED 執行緒會在 Owner 執行緒釋放鎖時喚醒
- WAITING 執行緒會在 Owner 執行緒呼叫 notify 或 notifyAll 時喚醒,但喚醒後並不意味者立刻獲得鎖,仍需進入EntryList 重新競爭
API:
- obj.wait() 讓進入 object 監視器的執行緒到 waitSet 等待
- obj.notify() 在 object 上正在 waitSet 等待的執行緒中挑一個喚醒
- obj.notifyAll() 讓 object 上正在 waitSet 等待的執行緒全部喚醒
sleep(long n) 和 wait(long n) 的區別:
-
sleep 是 Thread 方法,而 wait 是 Object 的方法
-
sleep 不需要強制和 synchronized 配合使用,但 wait 需要和 synchronized 一起用
-
sleep 在睡眠的同時,不會釋放物件鎖的,但 wait 在等待的時候會釋放物件鎖
-
它們狀態 TIMED_WAITING
演示:
1.示例程式碼
import lombok.extern.slf4j.Slf4j; import static com.dalianpai.Sleeper.sleep; /** * @author WGR * @create 2020/12/29 -- 14:57 */ @Slf4j(topic = "c.TestCorrectPosture") public class TestCorrectPostureStep1 { static final Object room = new Object(); static boolean hasCigarette = false; // 有沒有煙 static boolean hasTakeout = false; public static void main(String[] args) { new Thread(() -> { synchronized (room) { log.debug("有煙沒?[{}]", hasCigarette); if (!hasCigarette) { log.debug("沒煙,先歇會!"); sleep(2); } log.debug("有煙沒?[{}]", hasCigarette); if (hasCigarette) { log.debug("可以開始幹活了"); } } }, "小南").start(); for (int i = 0; i < 5; i++) { new Thread(() -> { synchronized (room) { log.debug("可以開始幹活了"); } }, "其它人").start(); } sleep(1); new Thread(() -> { synchronized (room) { hasCigarette = true; log.debug("煙到了噢!"); } }, "送煙的").start(); } }
其它幹活的執行緒,都要一直阻塞,效率太低
小南執行緒必須睡足 2s 後才能醒來,就算煙提前送到,也無法立刻醒來
加了 synchronized (room) 後,就好比小南在裡面反鎖了門睡覺,煙根本沒法送進門,main 沒加synchronized 就好像 main 執行緒是翻窗戶進來的
解決方法,使用 wait - notify 機制
2.示例程式碼
import lombok.extern.slf4j.Slf4j; import static com.dalianpai.Sleeper.sleep; /** * @author WGR * @create 2020/12/29 -- 15:13 */ @Slf4j(topic = "c.TestCorrectPosture") public class TestCorrectPostureStep2 { static final Object room = new Object(); static boolean hasCigarette = false; static boolean hasTakeout = false; public static void main(String[] args) { new Thread(() -> { synchronized (room) { log.debug("有煙沒?[{}]", hasCigarette); if (!hasCigarette) { log.debug("沒煙,先歇會!"); try { room.wait(2000); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("有煙沒?[{}]", hasCigarette); if (hasCigarette) { log.debug("可以開始幹活了"); } } }, "小南").start(); for (int i = 0; i < 5; i++) { new Thread(() -> { synchronized (room) { log.debug("可以開始幹活了"); } }, "其它人").start(); } sleep(1); new Thread(() -> { synchronized (room) { hasCigarette = true; log.debug("煙到了噢!"); room.notify(); } }, "送煙的").start(); } }
解決了其它幹活的執行緒阻塞的問題,但如果有其它執行緒也在等待條件呢?
3.示例程式碼
import lombok.extern.slf4j.Slf4j;
import static com.dalianpai.Sleeper.sleep;
/**
* @author WGR
* @create 2020/12/29 -- 15:17
*/
@Slf4j(topic = "c.TestCorrectPosture")
public class TestCorrectPostureStep3 {
static final Object room = new Object();
static boolean hasCigarette = false;
static boolean hasTakeout = false;
// 虛假喚醒
public static void main(String[] args) {
new Thread(() -> {
synchronized (room) {
log.debug("有煙沒?[{}]", hasCigarette);
if (!hasCigarette) {
log.debug("沒煙,先歇會!");
try {
room.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("有煙沒?[{}]", hasCigarette);
if (hasCigarette) {
log.debug("可以開始幹活了");
} else {
log.debug("沒幹成活...");
}
}
}, "小南").start();
new Thread(() -> {
synchronized (room) {
Thread thread = Thread.currentThread();
log.debug("外賣送到沒?[{}]", hasTakeout);
if (!hasTakeout) {
log.debug("沒外賣,先歇會!");
try {
room.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("外賣送到沒?[{}]", hasTakeout);
if (hasTakeout) {
log.debug("可以開始幹活了");
} else {
log.debug("沒幹成活...");
}
}
}, "小北").start();
sleep(1);
new Thread(() -> {
synchronized (room) {
hasTakeout = true;
log.debug("外賣到了噢!");
room.notifyAll();
}
}, "送外賣的").start();
}
}
用 notifyAll 僅解決某個執行緒的喚醒問題,但使用 if + wait 判斷僅有一次機會,一旦條件不成立,就沒有重新判斷的機會了
解決方法,用 while + wait,當條件不成立,再次 wait
4.示例程式碼
import lombok.extern.slf4j.Slf4j;
import static com.dalianpai.Sleeper.sleep;
/**
* @author WGR
* @create 2020/12/29 -- 15:19
*/
@Slf4j(topic = "c.TestCorrectPosture")
public class TestCorrectPostureStep5 {
static final Object room = new Object();
static boolean hasCigarette = false;
static boolean hasTakeout = false;
public static void main(String[] args) {
new Thread(() -> {
synchronized (room) {
log.debug("有煙沒?[{}]", hasCigarette);
while (!hasCigarette) {
log.debug("沒煙,先歇會!");
try {
room.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("有煙沒?[{}]", hasCigarette);
if (hasCigarette) {
log.debug("可以開始幹活了");
} else {
log.debug("沒幹成活...");
}
}
}, "小南").start();
new Thread(() -> {
synchronized (room) {
Thread thread = Thread.currentThread();
log.debug("外賣送到沒?[{}]", hasTakeout);
while (!hasTakeout) {
log.debug("沒外賣,先歇會!");
try {
room.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("外賣送到沒?[{}]", hasTakeout);
if (hasTakeout) {
log.debug("可以開始幹活了");
} else {
log.debug("沒幹成活...");
}
}
}, "小女").start();
sleep(1);
new Thread(() -> {
synchronized (room) {
hasTakeout = true;
log.debug("外賣到了噢!");
room.notifyAll();
}
}, "送外賣的").start();
}
}