为什么在下面的 Java 代码中:
public class WaitForOther {
private volatile boolean in = false;// need volatile for transitive closure
// to occur
public void stoppingViaMonitorWait() throws Exception {
final Object monitor = new Object();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (monitor) {
in = true;
try {
monitor.wait(); // this releases monitor
//Thread.sleep(8000); //No-op
System.out.println("Resumed in "
+ Thread.currentThread().getName());
} catch (InterruptedException ignore) {/**/
}
}
}
}).start();
System.out.println("Ready!");
while (!in); // spin lock / busy waiting
System.out.println("Set!");
synchronized (monitor) {
System.out.println("Go!");
monitor.notifyAll();
}
}
取消注释 Thread.sleep(8000);//No-op
导致缩短的输出:
准备好了!
放!
去吧!
否则会在中断的线程 0 中正确恢复:
Ready!
Set!
Go!
Resumed in Thread-0
这是调用上述行为的 JUnit 测试,正如评论中所要求的那样:
public class WaitForOtherTest {
WaitForOther cut = new WaitForOther();
@Test
public void testStoppingViaMonitorWait() throws Exception {
cut.stoppingViaMonitorWait();
}
}
谢谢!
最佳答案
我已经在 JUnit 中尝试过您的测试,但我得到的结果与您的体验相反:
- 当
Thread.sleep
被注释掉时,测试运行正常并打印“Resumed in <>” - 当
Thread.sleep
在代码中(实际执行)时,JVM 终止并且“Resumed in ...”不打印。
原因是 JUnit 在测试完成时终止了 VM。它执行 System.exit();
。您很幸运在案例 1 中获得了完整的输出,因为它是在单独的线程中打印的,并且 JUnit 不会等待该线程。
如果要确保线程在测试方法结束之前完成,要么需要让 API 等待线程,要么需要让测试等待线程。
如果您的stoppingViaMonitorWait
方法返回它创建的线程,您可以在测试中等待。
@Test
public void testStoppingViaMonitorWait() throws Exception {
Thread x = cut.stoppingViaMonitorWait();
x.join();
}
另一种选择是将一个线程池(ExecutorService
的一个实例)注入(inject)到您正在测试的类中,让它在池中安排它的线程(这在任何情况下都更好),在您的测试方法中,您可以调用 ExecutorService.awaitTermination
。
关于java - Java中的线程间通信,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39322831/