简体   繁体   English

Java中的线程间通信

[英]Inter-thread communication in Java

Why in the following Java code: 为什么在以下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();
    }

}

un-commenting of the Thread.sleep(8000); //No-op 取消评论Thread.sleep(8000); //No-op Thread.sleep(8000); //No-op results in a shortened output: Thread.sleep(8000); //No-op导致缩短输出:

Ready! Set! Go!

which otherwise correctly resumes in the interrupted Thread-0: 否则在中断的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: 这是JUnit测试,它调用上述行为,如评论中所要求的:

public class WaitForOtherTest {

    WaitForOther cut  = new WaitForOther();


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



}

Thanks! 谢谢!

I've tried your test in JUnit and I get the opposite from what you experienced: 我已经在JUnit中尝试过你的测试了,我从你的经历中得到了相反的结果:

  1. When Thread.sleep is commented away, the test runs fine and it prints "Resumed in <>" Thread.sleep被注释掉时,测试运行正常并打印“Resumed in <>”
  2. When Thread.sleep is in the code (actually executed), the JVM terminates and the "Resumed in ..." is not printed. Thread.sleep在代码中(实际执行)时,JVM终止并且不会打印“Resumed in ...”。

The reason is that JUnit terminates the VM when the tests are complete. 原因是JUnit在测试完成后终止VM。 It does a System.exit(); 它做了一个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. 你很幸运得到了案例1中的完整输出,因为它是在一个单独的线程中打印的,而JUnit并没有等待那个线程。

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. 如果要在测试方法结束之前确保Thread已完成,则需要让API等待线程,或者需要让测试等待线程。

If your stoppingViaMonitorWait method returns the Thread it creates, you can wait in the test. 如果你的stoppingViaMonitorWait方法返回它创建的Thread,你可以等待测试。

@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 . 另一种选择是将一个线程池( ExecutorService一个实例)注入到您正在测试的类中,让它在池上调度其线程(在任何情况下都更好),并且在您的测试方法中,您可以调用ExecutorService.awaitTermination

Your observation is not related to the Java memory model. 您的观察与Java内存模型无关。 Try running the following code: 尝试运行以下代码:

new Thread() {
  @Override
  public void run() {
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("Will not be printed when running JUnit");
  }
}.start();

If you run it in a main method, the thread - not being a deamon thread - keeps the process alive until the sleep period is over such that the last line is printed after which the process ends. 如果你在main方法中运行它,那么线程 - 不是deamon线程 - 使进程保持活动状态,直到睡眠周期结束,以便打印最后一行,然后进程结束。

As JUnit is testing potentially broken code, it needs to assume that a thread started from a unit test might never end. 由于JUnit正在测试可能损坏的代码,因此需要假设从单元测试启动的线程可能永远不会结束。 The framework will therefore not wait until all started threads terminate but explicitly terminate the Java process once all test methods returned. 因此,框架将不会等到所有启动的线程都终止,而是在返回所有测试方法后显式终止Java进程。 (Given you did not set a timeout for your tests where this time out applies additionally.) This way, the JUnit test suite is not vulnerable to broken code under test. (假设您没有为此次超时适用的测试设置超时。)这样,JUnit测试套件不容易受到测试中的破坏代码的影响。

However, if your test suite takes longer to execute than the started thread's runtime, you can still see the printed statement. 但是,如果您的测试套件执行时间比启动线程的运行时间长,您仍然可以看到打印的语句。 For example by adding another test: 例如,通过添加另一个测试:

public class WaitForOtherTest {

  WaitForOther cut = new WaitForOther();

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

  @Test
  public void testStoppingViaMonitorWait2() throws Exception {
    Thread.sleep(9000);
  }
}

you can still see the printed line. 你仍然可以看到印刷线。

Do however note that this print outcome is rather instable as it relies on a specific, non-deterministic test execution order and short runtimes for the non-waiting code. 但请注意,此打印结果相当不稳定,因为它依赖于特定的,非确定性的测试执行顺序和非等待代码的短运行时间。 (Yet, it will typically work for running this example what is good enought for demonstration purposes). (然而,它通常适用于运行这个示例,这对于演示目的来说是好的)。

This code misuses the Java's low-level thread communication constructs like wait and notify . 此代码滥用Java的低级线程通信结构,如waitnotify It is not clear what you want to establish with this code. 目前尚不清楚您希望使用此代码建立什么。

Here are my observations with your program's behavior (these are different from yours. I ran it both within an IDE and on command line using server and client compilers): 以下是我对程序行为的观察(这些与您的行为不同。我在IDE和命令行中使用服务器和客户端编译器运行它):

With the sleep() // commented out //: 随着sleep() // 注释掉 //:

Ready!
thread is daemon? : false
Set!
Go!
Resumed in Thread-0

With the sleep() uncommented : 使用sleep() 取消注释

Ready!
thread is daemon? : false
Set!
Go!
<< 8 seconds pass >>
Resumed in Thread-0

Thus, it is behaving per your design: The main thread starts a non-daemon thread (say thread-0 ). 因此,它符合您的设计: main线程启动非守护程序线程(比如thread-0 )。 The main thread and thread-0 then race for the lock ( monitor ), but thread-0 always wins out because you want it to. 然后main线程和thread-0竞争锁定( monitor ),但是thread-0总是胜出,因为你想要它。

If thread-0 grabs the lock, it sets the volatile write on in and immediately gives up the lock for the main thread to notify it by wait() ing on it. 如果thread-0抓起锁,它设置了性写in ,并立即放弃了锁main线程通过通知它wait()荷兰国际集团就可以了。 Now the main thread sees the volatile update (visibility guarantee), breaks out of the busy-wait, grabs the lock, prints "Go" and notifies thread-0 which, depending upon its sleep status gets notified. 现在main线程看到易失性更新(可见性保证),中断忙等待,抓取锁定,打印“Go”并通知thread-0 ,根据其睡眠状态得到通知。

The main thread won't win the race for the lock because of the busy wait. 由于忙碌的等待, main线程不会赢得锁定的竞争。

I added a line to clarify whether your thread is daemon or not and since the JVM is not exiting because your thread is a non-daemon thread, it stays long enough for the thread to wake up from the sleep and prints its line. 我添加了一行来澄清你的线程是否是守护进程 ,因为你的线程是一个非守护进程线程,因此JVM没有退出,它保持足够长的时间让线程从睡眠中唤醒并打印它的行。

It has been said that wait() should be used to only wait in a loop for a condition variable. 有人说应该使用wait()在一个循环中等待一个条件变量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM