Вывод потоков, не являющихся демонами, иногда исчезает, пока не используется join () - PullRequest
0 голосов
/ 27 июня 2018

У меня небольшой тест с использованием двух потоков следующим образом:

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() синхронизируется , а также я знаю, что потоки, созданные main thread, по умолчанию являются пользовательскими потоками, а пользовательские потоки не завершаются пока они не закончат.

Хотя я использую @ Test , я проверил, что они не-демон , как и ожидалось.

не-демон

является потоком демона, если и только если создающий поток является демоном

И

Виртуальная машина Java продолжает выполнять потоки, пока не произойдет одно из следующих действий:

  1. Вызван метод выхода класса Runtime, и менеджер безопасности разрешил выполнение операции выхода.

  2. Все потоки, которые не являются потоками демонов , умерли, либо возвращаясь из вызова метода run, либо выбрасывая исключение, которое распространяется за пределы метода run.

ОБНОВЛЕНО

На самом деле я знаю, на что указывают ответы, мне интересно, почему вывод исчезает?

Вопросы, которые мне нужно задать:

  1. Трубопровод , закрытый основным потоком?

  2. Почему? Это потому, что каждый конвейер связан с одним потоком независимо? Не поделился? (что конвейер не будет закрыт до тех пор, пока не закончится последний поток, как подсчет ссылок - файл не будет удален, пока его никто не использует)

  3. Последний вопрос: управляется ли он JVM или зависит от ОС?

Кажется, конвейер закрыт по @Test, который я упомянул. Спасибо, @meriton за то, что указал на это.

Заключение

Когда я попробовал этот метод в обычном main() и сразу вызвал System.exit(0); сразу после start(), вывод будет таким же, как при использовании @Test.

При использовании @Test (Junit) потоки не будут ждать. Для получения более подробной информации, пожалуйста, проверьте Тест JUnit не выполняет все потоки, созданные в тесте , и JUnit завершает дочерние потоки .

А вот объяснение от @Alex Lockwood, которое я лично предпочитаю:

JUnit - это фреймворк для юнит-тестирования ... как и в фреймворке Android, он имеет основной поток, из которого он будет вызывать определенные пользователем методы юнит-тестирования. Когда модульный тест возвращается, JUnit немедленно вызывает следующий метод модульного теста (или завершается полностью, если больше нет методов для модульного теста, которые нужно вызвать). JUnit ничего не знает о фоновых потоках, которые вы создаете / запускаете, поэтому вы не можете предполагать, что он будет сидеть сложа руки и ждать их завершения.

Ответы [ 2 ]

0 голосов
/ 27 июня 2018

Может ли исполнитель теста вызвать System.exit() после того, как все тесты будут выполнены? Можете ли вы воспроизвести частичный вывод, если преобразовать тест в обычный метод main()?

(я бы попробовал сам, но не смог воспроизвести частичный вывод при запуске этого теста JUnit в eclipse)

... и нет, System.out является статическим полем, и, следовательно, общим для всех потоков. Выход из основного потока не закрывает поток.

0 голосов
/ 27 июня 2018

Это потому, что вы запустили их в другом потоке, а main thread не ждет завершения child threads и завершит его выполнение сразу после последней строки.

Таким образом, к тому времени, если поток 1 будет выполнен, вывод будет:

First Ok

Если выполняется поток 2:

Second Ok

И если по счастливой случайности оба были казнены, то

First Ok 
Second Ok

или

Second Ok 
First Ok

Предоставляя join, вы просите main thread дождаться завершения child thread и, следовательно, обоих выходов.

- РЕДАКТИРОВАНИЕ -

Это не означает, что дочерние потоки были прерваны, они все еще завершили свое выполнение, но вы, возможно, не сможете увидеть результаты, так как к этому времени могли быть закрыты или освобождены outtream

Если вы выполните одну и ту же команду в Jshell, вы всегда получите второй вывод, но иногда он приходит как другая команда, потому что, как только основной поток завершает работу, Jshell переходит к следующей командной строке:

enter image description here

...