一、概述
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-hant/n/301038.html