首页 文章

非守护程序线程输出有时会消失,直到使用join()

提问于
浏览
1

我只用 two threads 进行了一个小测试,如下所示:

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

有时 outputonly 包含其中之一,然后在它们启动后引入 join() .

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

一个

First Ok

两个

First Ok
Second Ok

我知道 println()synchronized ,而且我知道 main 线程创建的线程是默认的用户线程,用户线程将 not exit 直到完成 .

虽然我正在使用@Test,但我按照预期测试它们是 non-daemon .

non-daemon

当且仅当创建线程是守护进程时,>是守护程序线程

Java虚拟机继续执行线程,直到发生以下任一情况:已调用类Runtime的exit方法,并且安全管理器已允许执行退出操作 . 所有非守护程序线程的线程都已死亡,无论是通过从run方法调用返回还是抛出传播超出run方法的异常 .

更新

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

我需要问的问题:

  • 管道由主线程关闭?

  • 为什么?是因为每个管道独立地绑定到一个线程吗?不共享? (直到最后一个线程完成就像 reference counting 一样,管道才会关闭 - 在没有人使用之前不会删除该文件)

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

似乎 pipeline 被我提到的 @Test 关闭了 . 谢谢,@ meriton指出了它 .

结论

当我在 ordinary 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对你创建/启动的后台线程一无所知,所以你不能认为它会坐下来等待它们完成 .

2 回答

  • 2

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

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

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

  • 2

    多数民众赞成是因为你在不同的线程上启动它们而且 main thread 不等待 child threads 完成,并且会在最后一行之后立即关闭它的执行 .

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

    First Ok
    

    如果执行了线程2:

    Second Ok
    

    如果幸运的话,两人都被执行了

    First Ok 
    Second Ok
    

    要么

    Second Ok 
    First Ok
    

    通过提供 join ,您要求 main thread 等待 child thread 完成,从而产生两个输出 .

    • 已编辑 -

    这并不意味着子线程被终止,它们仍然完成了它们的执行但是你可能无法看到结果,因为当时流出可能已被关闭或释放

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

    enter image description here

相关问题