Java 8 Concurrency Простейшая каноническая форма для основной задачи

У меня два вопроса: 1. Какая простейшая каноническая форма для Callable как задача в Java 8, захват и обработка результата? 2. В приведенном ниже примере, каков наилучший / самый простой / понятный способ держать основной процесс открытым, пока все задачи не будут выполнены?

Вот пример, который я имею до сих пор - это лучший подход в Java 8 или есть что-то более простое?

import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;

public class SimpleTask implements Supplier<String> {
  private SplittableRandom rand = new SplittableRandom();
  final int id;
  SimpleTask(int id) { this.id = id; }
  @Override
  public String get() {
    try {
      TimeUnit.MILLISECONDS.sleep(rand.nextInt(50, 300));
    } catch(InterruptedException e) {
      System.err.println("Interrupted");
    }
    return "Completed " + id + " on " +
      Thread.currentThread().getName();
  }
  public static void main(String[] args) throws Exception {
    for(int i = 0; i < 10; i++)
      CompletableFuture.supplyAsync(new SimpleTask(i))
        .thenAccept(System.out::println);
    System.in.read(); // Or else program ends too soon
  }
}

Есть ли более простой и понятный способ сделать это в Java-8? И как мне устранить System.in.read() в пользу лучшего подхода?

2 ответа

Решение

Канонический способ ожидания завершения нескольких CompletableFuture Например, создать новый в зависимости от всех CompletableFuture.allOf, Вы можете использовать это новое будущее, чтобы дождаться его завершения или запланировать новые последующие действия, как и с любым другим CompletableFuture:

CompletableFuture.allOf(
    IntStream.range(0,10).mapToObj(SimpleTask::new)
             .map(s -> CompletableFuture.supplyAsync(s).thenAccept(System.out::println))
             .toArray(CompletableFuture<?>[]::new)
).join();

Конечно, это всегда становится проще, если вы отказываетесь назначать уникальный идентификатор для каждой задачи. Так как ваш первый вопрос был о Callable Я покажу, как вы можете легко отправить несколько похожих задач, как Callable с помощью ExecutorService:

ExecutorService pool = Executors.newCachedThreadPool();
pool.invokeAll(Collections.nCopies(10, () -> {
    LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(
            ThreadLocalRandom.current().nextInt(50, 300)));
    final String s = "Completed on "+Thread.currentThread().getName();
    System.out.println(s);
    return s;
}));
pool.shutdown();

Служба исполнителя вернулась Executors.newCachedThreadPool() не делится и не останется в живых, даже если вы забудете вызвать shutDown(), но это может занять до одной минуты, прежде чем все потоки будут завершены.

Поскольку ваш первый вопрос буквально был: " Какая простейшая каноническая форма для запуска Callable как задачи в Java 8, захвата и обработки результата? ", Ответ может быть таким, что самая простая форма по-прежнему вызывает это call() метод напрямую, например

Callable<String> c = () -> {
    LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(
            ThreadLocalRandom.current().nextInt(50, 300)));
    return "Completed on "+Thread.currentThread().getName();
};
String result = c.call();
System.out.println(result);

Там нет более простого способа...

Рассмотрите возможность сбора фьючерсов в список. Тогда вы можете использовать join() на каждое будущее ждать их завершения в текущей теме:

List<CompletableFuture<Void>> futures = IntStream.range(0,10)
        .mapToObj(id -> supplyAsync(new SimpleTask(id)).thenAccept(System.out::println))
        .collect(toList());

futures.forEach(CompletableFuture::join);
Другие вопросы по тегам