【问题标题】:Non-daemon threads output sometimes disappear until join() being used非守护线程输出有时会消失,直到使用 join()
【发布时间】:2018-06-27 01:18:04
【问题描述】:

我有一个只使用 两个线程 的小测试,如下所示:

import static java.lang.System.out;


@Test
public void testTwoSimpleThreads() {
    doInParallelAsync(() -> {
                out.println("First Ok");
            },
            () -> {
                out.println("Second Ok");
            });
}

private void doInParallelAsync(Runnable first, Runnable second) {
    new Thread(() -> {
        first.run();
    }).start();

    new Thread(() -> {
        second.run();
    }).start();
}

在我介绍join() 之前,输出 包含其中之一,然后在它们启动之后。

到目前为止我遇到的两种不同的输出:

一个

First Ok

两个

First Ok
Second Ok

我知道println()同步的,而且我知道 ma​​in 线程创建的线程默认是用户线程,用户线程不会退出 直到他们完成。

虽然我使用的是 @Test,但我测试了它们是 非守护进程,正如预期的那样。

non-daemon

当且仅当创建线程是守护线程时才是守护线程

Java 虚拟机继续执行线程,直到发生以下任一情况:

  1. 已调用 Runtime 类的退出方法,并且安全管理器已允许进行退出操作。

  2. 所有不是守护线程的线程都已经死亡,要么是从对 run 方法的调用返回,要么是抛出一个传播到 run 方法之外的异常。

更新

其实我知道答案指出了什么,不知道为什么输出消失了?

我需要问的问题:

  1. 主线程关闭的管道

  2. 为什么?是因为每个管道都独立地绑定到一个线程吗?不共享? (在最后一个线程完成之前管道不会关闭,就像 引用计数 - 文件不会被删除,直到没有人使用它)

  3. 最后一个问题:它是由JVM控制还是依赖于操作系统?

管道似乎被我提到的@Test 关闭了。谢谢@meriton 指出。

结论

当我在普通main()中尝试此方法时,直接在start()之后调用System.exit(0);,输出与使用@Test时的输出完全相同。

当使用@Test (Junit) 时,线程不会被等待。更多详情请查看JUnit test not executing all threads created within the testJUnit terminates child threads

这是我个人更喜欢的@Alex Lockwood 的解释:

JUnit 是一个单元测试框架...与 Android 框架一样,它有一个主线程,从中可以调用用户定义的单元测试方法。当单元测试返回时,JUnit 立即调用下一个单元测试方法(如果没有更多单元测试方法可以调用,则完全退出)。 JUnit 对您创建/启动的后台线程一无所知,因此您不能假设它会坐在那里等待它们完成。

【问题讨论】:

  • 输出将只包含一个总是第一个有时是第二个?
  • 您没有展示out 是什么或您如何创建它。请添加。
  • @ScaryWombat 我刚刚更新了我的问题,请看一下好吗?谢谢~
  • @Hearen 是的,同意,但由于主线程已经完成,将不再工作。例如在 Jshell 上尝试同样的事情,你总是会得到两个输出,但第二个输出有时会作为另一个命令,这是因为主线程没有等待子线程关闭并完成,因此 jshell 移动到下一个命令可以看到其他输出的行
  • 请阅读并回复上面的第二条评论。我猜main 线程会隐式打开 closes out

标签: java multithreading junit junit4 println


【解决方案1】:

在所有测试都运行之后,测试运行器是否会调用System.exit()?如果将测试转换为普通的main() 方法,能否重现部分输出?

(我自己尝试过,但在 Eclipse 中将其作为 JUnit 测试运行时无法重现部分输出)

...不,System.out 是一个静态字段,因此由所有线程共享。退出主线程不会关闭流。

【讨论】:

  • 非常感谢,meriton。我只是不能在普通的主程序中重新生成 in-complete 输出。似乎是 runner 关闭它。
【解决方案2】:

那是因为您在不同的线程上启动了它们,而 main thread 不会等待 child threads 完成,并且会在最后一行之后立即关闭它的执行。

所以,到那时如果线程 1 被执行,输出将是:

First Ok

如果线程 2 被执行:

Second Ok

如果运气好,两人都被处决了,那么

First Ok 
Second Ok

Second Ok 
First Ok

通过提供join,您要求main thread 等待child thread 完成并因此完成两个输出。

-- 已编辑--

这并不意味着子线程已终止,它们仍然完成了执行,但您可能无法看到结果,因为此时 outstream 可能已关闭或释放

如果你在 Jshell 上执行相同的命令,你总是会得到第二个输出,但有时它会作为另一个命令进来,因为一旦主线程完成,Jshell 就会移动到下一个命令行:

【讨论】:

  • 感谢您的回复,但实际上并没有解决我的问题。
  • @Hearen 为什么不呢?在终止main 线程之前,您需要等待每个线程终止。您可以使用 join 或 countdownLatch 来做到这一点
  • @ScaryWombat 我更新了我的问题。请检查一下好吗?终止时间可以理解,但我的困惑不在这里。
  • @Hearen 我认为你需要重新表述你的问题,如果你问的是不同的问题,因为现在,你似乎在问为什么两个线程都没有显示输出,我已经分享相同的原因
  • @AmanChhabra 感谢您的挖掘。很抱歉这么说:仍然没有回答我更新的问题。谢谢你,阿曼。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多