了解 wait() 和 notify() 方法

fra*_*kee 2 java concurrency multithreading

我试图了解 Javawaitnotify方法的工作原理。根据文档,wait()导致线程等待对notify()notifyAll()方法的后续调用,但由于某种原因notify不会中断“等待”:

public static void main(String[] args) {

    Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("thread1 is started, waiting for notify()");

            synchronized (this) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    System.out.println(e.getLocalizedMessage());
                }
            }

            System.out.println("waiting is over");
        }
    });

    thread1.start();

    // unblock thread1 in 2 seconds

    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    synchronized (thread1) {
        thread1.notify();
    }
}
Run Code Online (Sandbox Code Playgroud)

dav*_*mac 5

您需要notify的是正在wait编辑的对象,而不是正在等待的线程。

在您的情况下,对象waited 是匿名内部类的实例,这是有问题的,因为您无法轻松获得对它的引用notify。您可以通过Thread直接扩展来解决这个问题:

Thread thread1 = new Thread() {
    @Override
    public void run() {
        System.out.println("thread1 is started, waiting for notify()");

        synchronized (this) {
            try {
                wait();
            } catch (InterruptedException e) {
                System.out.println(e.getLocalizedMessage());
            }
        }

        System.out.println("waiting is over");
    }
};
Run Code Online (Sandbox Code Playgroud)

现在this(in synchronized (this)) 指的是线程本身,并且wait也在线程对象上调用了。在这种情况下,您当前的调用notify应该没问题,因为它通知同一个对象(在这种情况下发生的是正在等待的线程 - 但要清楚的是,情况不一定如此)。

将一个对象用于同步,但也可能在其他地方使用,这被认为不是一种好的做法;Thread 的实例就是一个例子,实际上文档特别建议不要这样做:

建议应用程序无法使用waitnotifynotifyAllThread实例。

此外,您应该正确处理虚假唤醒;也就是说,wait可能会因为notify/notifyAll在别处被调用或者甚至根本没有被调用而返回。正如文档中所说

线程也可以在没有被通知、中断或超时的情况下唤醒,即所谓的虚假唤醒。虽然这在实践中很少发生,但应用程序必须通过测试应该导致线程被唤醒的条件来防止它,如果条件不满足则继续等待。换句话说,等待应该总是发生在循环中 [...]

因此,您的示例应该真正使用一个单独的变量来跟踪唤醒是否是有意的(由于显式notify)。