一、概述
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()语义的正确性。