" notifyAll的()"在java线程中:意外的等待时间

时间:2018-01-15 11:37:29

标签: java

我正在阅读OCA / OCP Java SE 7程序员I& II学习指南,我被困在一个例子:

package threads;
class Totalizer implements Runnable
{
    int total = 0;
    public void run(){
        synchronized(this){
            for(int i = 0; i < 100; i++){
                total += i;     
            }
            notifyAll();
        }   
    }   
}
class Tester extends Thread
{
    Totalizer t;
    public Tester(Totalizer tot){t = tot;}
    public void run(){
        synchronized(t){
            try {
                System.out.println("Waiting for calculation...");
                t.wait();
            } catch (InterruptedException e) {}
            System.out.println(t.total);
        }
    }
    public static void main(String[] args){
        Totalizer t = new Totalizer();
        new Tester(t).start();
        new Tester(t).start();
        new Tester(t).start();
    }
}
//

当我运行main()时,它会打印:

waiting for calculation...
waiting for calculation...
waiting for calculation...

没有任何反应,没有计算,没有。我无法弄清楚这段代码有什么问题。

1 个答案:

答案 0 :(得分:2)

两点。

最明显的一点是你永远不会启动Totalizer runnable,因此永远不会发出notifyAll调用。你需要一行

new Thread(t).start();

main方法中的某处。但即使你这样做,它也无法可靠地工作,因为wait调用可以在<{em> notifyAll调用之后调用。它也可能过早打印输出,因为wait调用也可以在没有notifyAll的情况下唤醒。

Javadoc for Object.wait()描述了您需要做的事情:

synchronized (obj) {
    while (<condition does not hold>)
        obj.wait();
    ... // Perform action appropriate to condition
}

因此,如果要正确使用它,就不能像这样调用Object.wait。这是因为:

  • 在开始等待之前,您不知道条件是否已经满足
  • 等待通话也可能在没有通知电话的情况下被唤醒

在您的情况下,您需要一个可以检查的条件变量。例如,您可以像这样更改代码:

class Totalizer implements Runnable
{
    int total = 0;
    boolean calculationComplete; // Condition to check in wait()
    public void run() {
        for(int i = 0; i < 100; i++) {
            total += i;     
        }
        synchronized (this) {
            // Indicate condition for wait() is now true
            calculationComplete = true;
            notifyAll();
        }   
    }   
}
class Tester extends Thread
{
    Totalizer t;
    public Tester(Totalizer tot){t = tot;}
    public void run(){
        synchronized(t) {
            System.out.println("Waiting for calculation...");
            // Loop, terminate when condition is true
            while (!t.calculationComplete) {
                try {
                    t.wait();
                } catch (InterruptedException e) {}
            }

            System.out.println(t.total);
        }
    }