Simeon Leyzerzon Simeon Leyzerzon - 2 months ago 14
Java Question

Inter-thread communication in Java

Why in the following Java code:

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();
}

}


un-commenting of the
Thread.sleep(8000); //No-op
results in a shortened output:

Ready!
Set!
Go!


which otherwise correctly resumes in the interrupted Thread-0:

Ready!
Set!
Go!
Resumed in Thread-0


Here's the JUnit test which invokes the above behavior, as asked for in the comments:

public class WaitForOtherTest {

WaitForOther cut = new WaitForOther();


@Test
public void testStoppingViaMonitorWait() throws Exception {
cut.stoppingViaMonitorWait();
}



}


Thanks!

Answer

I've tried your test in JUnit and I get the opposite from what you experienced:

  1. When Thread.sleep is commented away, the test runs fine and it prints "Resumed in <>"
  2. When Thread.sleep is in the code (actually executed), the JVM terminates and the "Resumed in ..." is not printed.

The reason is that JUnit terminates the VM when the tests are complete. It does a System.exit();. You're lucky that you get the full output in case 1., because it's printed in a separate thread, and JUnit is not waiting for that thread.

If you want to make sure that the Thread is complete before the end of the test method, either you need to have your API wait for the thread, or you need to have the test wait for the thread.

If your stoppingViaMonitorWait method returns the Thread it creates, you can wait in the test.

@Test
public void testStoppingViaMonitorWait() throws Exception {
    Thread x = cut.stoppingViaMonitorWait();
    x.join();
}

Another option is that you inject a thread pool (an instance of ExecutorService) into the class that you're testing, have it schedule its thread on the pool (that's nicer in any case), and in your test method you can call ExecutorService.awaitTermination.

Comments