一、概述
wait()是Java中的一個關鍵字,用於線程之間的協作,線程可以調用wait()方法釋放對象鎖並進入等待狀態,直到其他線程調用對象的notify()或notifyAll()方法來喚醒這個線程。wait()方法需要放在synchronized代碼塊中或者synchronized方法中,否則會拋出IllegalMonitorStateException異常。
wait()方法應該是使用線程之間的協作、互斥、同步的手段,而不是用於線程之間的通信。
二、wait()和notify()/notifyAll()的基本用法
wait()方法會將當前線程掛起,讓出鎖。調用wait()方法時,會依據實例對象上的鎖。notify()和notifyAll()方法用於喚醒正在等待該實例對象的線程。notifyAll()會喚醒所有正在等待該實例對象的線程,而notify()會喚醒其中的一個。
public class WaitNotifyDemo {
private final Object lock = new Object();
private void waitForSignal() {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread " + Thread.currentThread().getName() + " get notified");
}
}
private void notifyThread() {
synchronized (lock) {
lock.notify();
}
}
public static void main(String[] args) throws InterruptedException {
WaitNotifyDemo demo = new WaitNotifyDemo();
new Thread(demo::waitForSignal, "thread1").start();
Thread.sleep(1000);
demo.notifyThread();
}
}
三、wait()的超時時間
wait(long timeout)方法會在指定的超時時間內等待,如果超時還未被喚醒,線程將自動喚醒。另外,notify()或notifyAll()的調用並不需要自己持有鎖,而是需要鎖釋放才能夠發揮作用。
public class WaitNotifyTimeoutDemo {
private final Object lock = new Object();
private void waitForSignal(long timeout) {
synchronized (lock) {
try {
lock.wait(timeout);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread " + Thread.currentThread().getName() + " get notified");
}
}
private void notifyThread() {
synchronized (lock) {
lock.notify();
}
}
public static void main(String[] args) throws InterruptedException {
WaitNotifyTimeoutDemo demo = new WaitNotifyTimeoutDemo();
new Thread(() -> demo.waitForSignal(1000), "thread1").start();
Thread.sleep(3000);
}
}
四、競爭條件下的wait()
由於wait()方法必須放在synchronized代碼塊中,因此在多個線程同時競爭一個鎖對象時,可能發生線程意外喚醒的情況。這時候需要在相應的代碼中加入額外的判斷語句,以防止虛假喚醒。 Java線程庫提供了一個ConcurrentLock來解決這個問題,線程可以使用它來防止虛假喚醒。
public class WaitNotifyRaceConditionDemo {
private final Object lock = new Object();
private boolean isSignaled = false;
private void waitForSignal() {
synchronized (lock) {
while(!isSignaled) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Thread " + Thread.currentThread().getName() + " get notified");
}
}
private void notifyThread() {
synchronized (lock) {
isSignaled = true;
lock.notify();
}
}
public static void main(String[] args) throws InterruptedException {
WaitNotifyRaceConditionDemo demo = new WaitNotifyRaceConditionDemo();
new Thread(demo::waitForSignal, "thread1").start();
Thread.sleep(1000);
new Thread(demo::waitForSignal, "thread2").start();
Thread.sleep(1000);
new Thread(demo::waitForeverAndNeverBeNotified, "thread3").start();
Thread.sleep(1000);
demo.notifyThread();
Thread.sleep(1000);
}
private static void waitForeverAndNeverBeNotified() {
final WaitNotifyRaceConditionDemo demo = new WaitNotifyRaceConditionDemo();
demo.waitForSignal();
}
}
五、wait()的注意點
1、wait()方法本身可以被中斷,即在wait()狀態下調用interrupt()方法會是線程拋出InterruptedException異常;
2、wait()方法和synchronized代碼塊的使用應該遵循一個原則:wait()方法和notify()/notifyAll()方法的使用應該且只應該在對象的所有者線程(鎖相關的代碼操作的線程)中使用;
3、wait()和notify()/notifyAll()的語義需要開發者精細把控,例如代碼中WaitNotifyRaceConditionDemo示例中,在notifyThread()方法中,需要先將notify()之後再修改狀態。
六、結論
wait()是Java中的一個重要關鍵字,用於線程之間的協作,線程可以調用wait()方法釋放對象鎖並進入等待狀態,直到其他線程調用對象的notify()或notifyAll()方法來喚醒該線程。wait()方法需要放在synchronized代碼塊中或者synchronized方法中,否則會拋出IllegalMonitorStateException異常。wait()方法應該是使用線程之間的協作、互斥、同步的手段,而不是用於線程之間的通信。在使用wait()方法和notify()/notifyAll()方法的時候需要注意競爭條件,需要額外加判斷以及確保wait()和notify()/notifyAll()語義的正確性。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/301038.html
微信掃一掃
支付寶掃一掃