您的位置:

Java中的wait()

一、概述

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