这有什么问题?

时间:2015-01-10 13:13:08

标签: java multithreading

我见过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"

2 个答案:

答案 0 :(得分:0)

问题是,您没有调用m方法,因此永远不会调用notify,因此您的线程会永远休眠。您可以在开始之后使用main

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

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

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

与问题没有密切关系,但throws中的main声明不太明确,即使生成的printStackTrace比抛出异常更好。

答案 1 :(得分:0)

您在main中创建的主题调用MyThread#k()进入等待状态。此时,该线程在被唤醒或中断之前不会执行任何其他操作。但是代码中可能被唤醒的唯一位置是notify中的MyThread#m()。由于程序中没有任何内容调用该方法,因此线程永远不会被唤醒。

您可能希望在主程序中s.m()之后立即添加对s.start()的调用。这样你的主线程就会执行唤醒你的线程所需的notify

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

您可以尝试在主程序中Thread.sleep(1000)之前添加s.m()来解决此问题。这几乎肯定会起作用,因为你的主线程将产生执行,让你的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();
        }
    }
}