它有什么问题?

What is wrong with it?

我看过很多 waitnotify 的例子,但我还是有问题。

public class Main(){
    public static void main(String args[]) throws Exception {
        MyThread s = new MyThread();
        s.start();
    }
}

class MyThread extends Thread {
    public void run() {
        k();
    }

    public synchronized void k() {
        System.out.println("before wait");
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("do something after wait");
    }

    public synchronized void m() {
        for (int i=0;i<6;i++)
            System.out.println(i);
        notify();
    }
}

程序 运行 时我得到的唯一输出是:"before wait".

问题是,你没有调用你的 m 方法,所以 notify 永远不会被调用,所以你的线程永远休眠。您可以在开始后使用 s.m():

main 中调用它
MyThread s = new MyThread();
s.start();
s.m();

也许你应该在调用 m 方法之前 sleep 一段时间,因为它可以 运行 比线程中的 k 更快:

s.start();
try {
    Thread.sleep(200);
} catch (InterruptedException e) {
    // nothing to do
}
s.m();

与问题没有密切关系,但是 main 中的 throws 声明不是很可取,即使是生成的 printStackTrace 也比将异常丢弃更好。

您在 main 中创建的线程调用 MyThread#k() 进入等待状态。那时,该线程将不会做任何其他事情,直到它被唤醒或中断。但是在您的代码中唯一可能被唤醒的地方是 MyThread#m() 中的 notify。由于您的程序中没有任何内容调用该方法,因此线程永远不会被唤醒。

您可能想要在主程序中的 s.start() 之后添加对 s.m() 的调用。这样您的主线程将执行唤醒您的线程所需的 notify

不幸的是,这不太可能奏效。问题是 s.start() 导致您创建的线程准备好 运行,但不一定立即 运行。您对 s.m() 的调用很可能会在创建的线程执行任何操作之前完成。然后你仍然会得到与之前完全相同的结果,只是你会看到在 before wait 之前打印出整数 0..6。 notify 将不执行任何操作,因为子线程尚未执行其 wait。 (顺便说一下,因为 MyThread#k()MyThread#m() 都是同步的,所以增加 MyThread#m() 中的循环限制不会改变任何事情......子线程将无法输入 MyThread#k()MyThread#m() 是 运行ning。您可以通过将 notify 放在同步块中而不是使所有 MyThread#m() 同步来改进它。)

您可以尝试通过在主程序中的 s.m() 之前添加 Thread.sleep(1000) 来解决这个问题。这几乎肯定会起作用,因为您的主线程将让出执行,让您的 JVM 有机会安排子线程进行一些有用的工作。当主线程从睡眠中醒来并执行它的 s.m() 调用时,子线程可能已经执行了它的 wait 然后你会看到你的 do something after wait 消息。

但这仍然很糟糕,因为它仍然取决于您实际上无法控制的安排事件。仍然保证 wait 会在 notify 之前发生。

这就是为什么在使用 wait/notify 时,您通常应该安排某种可靠的测试,以确定您等待完成的事情是否真的发生了。这应该是一个条件,一旦它变为真,将至少在随后执行测试之前保持真。然后你典型的等待循环看起来像这样:

while (!isDone()) {
    synchronized(monitorObject) {
        try {
            monitorObject.wait();
        } catch (InterruptedException e) {
        }
    }
}

将整个事情放在一个循环中可以解决过早醒来的问题,例如由于 InterruptedException。

如果在执行此代码时所需的工作已经发生,则不会发生 wait,并且执行该工作的代码执行的 notify 是空操作。否则,此代码将等待,完成工作的代码最终将执行 notify 以根据需要唤醒此代码。当然,至关重要的是,在执行 notify 时,等待条件(上面的 isDone())为真并且至少在测试之前保持为真。

这是您的代码的更正版本,其中包含一个适当的等待循环。如果注释掉 Thread.sleep() 调用,您可能看不到 waiting 消息,因为工作会在等待循环开始之前完成。包括睡眠在内,您可能会看到 waiting 消息。但无论哪种方式,程序都会正常运行。

public static void main(String[] argv) throws Exception {
    MyThread s = new MyThread();
    s.start();
    Thread.sleep(1000);
    s.m();
}

class MyThread extends Thread {

    @Override
    public void run() {
        k();
    }

    private boolean done = false;

    public void k() {
        System.out.println("before wait");
        while (!done) {
            System.out.println("waiting");
            synchronized (this) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }

        System.out.println("do something after wait");
    }

    public void m() {
        for (int i = 0; i < 6; i++) {
            System.out.println(i);
        }
        synchronized (this) {
            done = true;
            notify();
        }
    }
}