下面是示例程序的一个版本,它改为引入一个测试条件变量的循环。在从等待苏醒这样你就避免对事物的状态后一个线程坏的假设重新获取一个锁,而且也没有两个线程之间的顺序关系:
public class W extends Thread {
long sum;
boolean done;
public static void main(String[] args) throws InterruptedException {
W w = new W();
w.start();
synchronized(w) {
while (!w.done) {
w.wait();
}
// move to within synchronized block so sum
// updated value is required to be visible
System.out.println(w.sum);
}
}
@Override public synchronized void run() {
for (int i = 0; i < 1000000; i++) {
sum += i;
}
done = true;
// no notify required here, see nitpick at end
}
}
这是不够的等待通知,因为你指出的原因(顺序依赖性,你依赖的是竞争条件,希望一个线程在另一个线程之前获取监视器)以及其他原因。首先,一个线程可以在没有收到通知的情况下从等待中唤醒,但是你不能认为有任何通知。
当一个线程等待时,它需要在一个循环中完成,在循环的测试中,它检查一些条件。另一个线程应该设置该条件变量,以便第一个线程可以检查它。 the Oracle tutorial的建议是:
注意:始终在测试等待条件的循环中调用wait。不要认为中断是针对您正在等待的特定情况,或者情况依然如此。
其他吹毛求疵:
当你的例子写的,JVM不需要进行更改您的总和变量可见的主线程。如果添加一个同步实例方法来访问sum变量,或者访问同步块内的和,那么主线程将保证能够看到sum的更新值。
看着你的日志记录,没有什么关于InterruptedException的严重问题,它并不意味着任何错误。当您在线程上调用中断,设置其中断标志,并且该线程当前正在等待或正在休眠,或者在标志仍然置位时进入等待或休眠方法时,会导致InterruptedException。在我的答案顶部的玩具示例中,我将异常置于throws子句中,因为我知道这不会发生。
当线程终止时,它发出一个notifyAll,任何等待该对象的东西都会收到(同样,这就是连接的实现方式)。部分原因是使用Runnable而不是Thread。
在这个特殊的例子中,在求和线程上调用Thread#join会更有意义,而不是调用wait。
这里的例子重新编写,使用加入代替:
public class J extends Thread {
private long sum;
synchronized long getSum() {return sum;}
public static void main(String[] args) throws InterruptedException {
J j = new J();
j.start();
j.join();
System.out.println(j.getSum());
}
@Override public synchronized void run() {
for (int i = 0; i < 1000000; i++) {
sum += i;
}
}
}
线程#加入呼叫等待,锁定线程对象。当求和线程终止时,它发送一个通知并将其isAlive标志设置为false。同时在join方法中,主线程正在等待求和线程对象,它接收到通知,检查isAlive标志,并且实现它不必再等待,因此它可以离开join方法并打印结果。
从这些基本的API转移到高级API,如java.util.concurrent.Executors和ExecutorService。查看代码示例:http://examples.javacodegeeks.com/core-java/util/concurrent/executorservice/java-executorservice-example-tutorial/ –
@ sunrise76,java.util.concurrent中的类不是“更高级的:“他们在更高级别的抽象层面上运作。编写生产代码的devloper绝对应该使用更高级别的工具,但是_student_可以很好地理解构建这些更高级别工具的原语。就像学过汇编语言的人在用更高级的语言编写时做出更明智的决定一样,所以了解互斥锁,条件变量和原子操作的人可以更好地使用队列和线程池等。 –
您的理解是正确的。如果没有其他线程在同一时间在'foo.wait()'调用中被阻塞,'foo.notify()'什么也不做。 –