理解wait()和notify()方法

时间:2017-03-24 09:14:07

标签: java multithreading concurrency

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

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();
    }
}

2 个答案:

答案 0 :(得分:4)

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

在你的情况下,对象wait是一个匿名内部类的实例,这是有问题的,因为你不能轻易获得它的引用,以便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");
    }
};

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

使用可能在其他地方使用的同步对象并不是一种良好的做法; Thread的实例就是这样的一个例子,事实上documentation特别反对它:

  

建议应用程序不要在wait个实例上使用notifynotifyAllThread

另外,你应该正确处理虚假唤醒;也就是说,wait可能会返回,因为notify / notifyAll在其他地方被调用,或者甚至根本不被调用。作为文档also says

  

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

因此,您的示例应该使用单独的变量来跟踪唤醒是否是故意的(由于明确的notify)。

答案 1 :(得分:1)

  

由于某种原因,通知不会中断“等待”:

@ davmac的答案是正确的,但对于后代,还有其他一些方法可以做到这一点,因为在Thread对象上扩展wait()并调用notify()Thread是不推荐。

最好的方法是创建一个锁对象。制作锁定对象final总是一个很好的模式,尽管这里也需要在内部类中使用它。

final Object lock = new Object();
Thread thread1 = new Thread(new Runnable() {
    ...
        synchronized (lock) {
            try {
                lock.wait();
            } catch (InterruptedException e) {
                // always a good pattern
                Thread.currentThread().interrupt();
                System.out.println(e.getLocalizedMessage());
            }
        }
      ...
}
...
synchronized (lock) {
    lock.notify();
}
// might as well wait for it to finish
thread1.join();