Java8; Используйте время сна на одном потоке, но несколько вызываемых - PullRequest
3 голосов
/ 06 марта 2020

Возможно ли в стандартном java8 одновременное выполнение нескольких вызовов в одном потоке?
т.е. когда один вызываемый объект спит, начните работать с другим объектом.

Мой текущий эксперимент, который не работает:

    ExecutorService executor = Executors.newSingleThreadExecutor();
    List<Future> fs = new ArrayList<>();
    for (int i = 0; i < 2; i++) {
        final int nr = i;
        fs.add(executor.submit(() -> {
            System.out.println("callable-" + nr + "-start");
            try { Thread.sleep(10_000); } catch (InterruptedException e) { }
            System.out.println("callable-" + nr + "-end");
            return nr;
        }));
    }
    try { executor.awaitTermination(5, TimeUnit.SECONDS); } catch (InterruptedException e) { }

Результаты:

callable-0-start
callable-0-end
callable-1-start
callable-1-end

Я хочу получить:

callable-0-start
callable-1-start
callable-0-end
callable-1-end

Примечания:

  • Я как бы ожидаю ответа : "Нет, это невозможно. Это не так, как работают потоки. Как только поток назначен какому-либо исполняемому коду, он запускается до завершения, исключения или отмены. Не может быть никакого переключения в полете между вызовами / runnables. Thread.sleep позволяет только другие потоки для запуска на процессоре / ядре. " (явное подтверждение заставило бы мой ум успокоиться)
  • Естественно, это "игрушечный" пример.
  • Это касается понимания, а не какой-то конкретной проблемы c, которая у меня есть.

Ответы [ 2 ]

1 голос
/ 09 марта 2020

Опираясь на ответ @ TreffnonX

Одним из способов достижения желаемого результата стандартного вывода является использование CompletableFuture
(вызываемый код должен быть явно разделен на отдельные функции):

    ExecutorService executor = Executors.newSingleThreadExecutor();
    CompletableFuture<Integer>[] fs = new CompletableFuture[2];
    for(int i=0; i<2; i++) {
        final Integer ii = i;
        fs[i] = (CompletableFuture.completedFuture(ii)
                .thenApply((Integer x) -> { System.out.println("callable-" + x + "-start");return x; })
                .thenApplyAsync((Integer x) -> { try { Thread.sleep(1_000); } catch (InterruptedException e) {Thread.currentThread().interrupt();} return x; }, executor)
                .thenApply((Integer x) -> { System.out.println("callable-" + x + "-end");return x; }));
    }
    CompletableFuture.allOf(fs).join();
    try { executor.awaitTermination(5, TimeUnit.SECONDS); } catch (InterruptedException e) { }

Результат :

callable-0-start
callable-1-start
callable-0-end
callable-1-end
1 голос
/ 06 марта 2020

То, что вы пытаетесь сделать, это эмулировать устаревшую функциональность из более старых java версий. Тогда можно было остановить, приостановить или возобновить Thread. Но из javado c из Thread.stop:

Этот метод небезопасен по своей природе. Остановка потока с помощью Thread.stop заставляет его разблокировать все заблокированные мониторы (как естественное следствие неконтролируемого исключения ThreadDeath, распространяющегося вверх по стеку). Если какой-либо из объектов, ранее защищенных этими мониторами, находился в несогласованном состоянии, поврежденные объекты становятся видимыми для других потоков, что может привести к произвольному поведению. Многие варианты использования stop должны быть заменены кодом, который просто изменяет некоторую переменную, чтобы указать, что целевой поток должен прекратить работу. Целевой поток должен регулярно проверять эту переменную и упорядоченно возвращаться из своего метода run, если переменная указывает, что она должна прекратить работу. Если целевой поток ожидает в течение длительных периодов (например, для переменной условия), для прерывания ожидания следует использовать метод прерывания.

Как описано в этом выводе, риски выполнения того, что вы хочу, чтобы они были критически важны, и поэтому это поведение устарело.

Я хотел бы предложить, чтобы вместо попытки заставить запущенный поток в какую-то остановочную позицию извне, вам, возможно, следует подумать о API ThreadPool, который позволяет вам правильно упаковать сегменты кода, чтобы их состояние могло быть выгружено из потока, а затем возобновлено. например, создать Ticket, который будет элементарным заданием, которое поток всегда будет выполнять перед началом другого, TicketChain, который последовательно соединяет заявки и сохраняет состояние. Затем создайте обработчик, который обрабатывает заявки по очереди. Если Билет в настоящее время не может быть выполнен (например, из-за того, что не все данные присутствуют или какая-либо блокировка не может быть получена), поток может пропустить его до более позднего момента времени, когда указанные условия могут быть истинными.

...