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