Ответ Ивана Гаммеля не точен.
На самом деле нет исполнителя, связанного с CompletableFuture
, возвращаемым allOf()
, как на самом деле никогда не бывает исполнителя, связанного с каким-либо CompletableFuture
.
задача связана с исполнителем, так как она выполняется внутри него, но связь обратная: у исполнителя есть список задач для выполнения.
Задачу также можно связать с CompletableFuture
, которая будет завершена, когда задача завершится. Сам CompletableFuture
не хранит ссылку на задачу или исполнителя, которые использовались для его создания. Однако он может хранить ссылки на задачи и, возможно, исполнители, используемые на зависимых этапах.
CompletableFuture
, возвращенный allOf()
, будет завершен задачей, которая является зависимой стадией исходных CompletableFuture
s. В вашем примере эта задача может быть выполнена:
executor1
, если третья задача завершилась первой;
executor2
, если 2 первых задания завершились раньше третьего; или
- исходный поток, если все задачи были завершены до того, как вы вызвали
allOf()
.
Это можно увидеть, добавив зависимый этап thenRun()
к вызову allOf()
:
public class CompletableFutureAllOfCompletion {
private ExecutorService executor1 = Executors.newFixedThreadPool(2);
private ExecutorService executor2 = Executors.newFixedThreadPool(2);
private Random random = new Random();
public static void main(String[] args) {
new CompletableFutureAllOfCompletion().run();
}
public void run() {
CompletableFuture<Integer> cf1 = supplyAsync(this::randomSleepAndReturn, executor1);
CompletableFuture<Integer> cf2 = supplyAsync(this::randomSleepAndReturn, executor1);
CompletableFuture<Integer> cf3 = supplyAsync(this::randomSleepAndReturn, executor2);
randomSleepAndReturn();
CompletableFuture.allOf(cf1, cf2, cf3)
.thenRun(() -> System.out.println("allOf() commpleted on "
+ Thread.currentThread().getName()));
executor1.shutdown();
executor2.shutdown();
}
public int randomSleepAndReturn() {
try {
final long millis = random.nextInt(1000);
System.out.println(
Thread.currentThread().getName() + " waiting for " + millis);
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 0;
}
}
Некоторые возможные выходы:
Завершение на первом исполнителе (третья задача завершена первой):
pool-1-thread-1 waiting for 937
pool-1-thread-2 waiting for 631
main waiting for 776
pool-2-thread-1 waiting for 615
allOf() commpleted on pool-1-thread-1
Завершение на втором исполнителе (первая и вторая задача завершены раньше третьей):
pool-1-thread-1 waiting for 308
pool-1-thread-2 waiting for 788
main waiting for 389
pool-2-thread-1 waiting for 863
allOf() commpleted on pool-2-thread-1
Завершение в основном потоке (все задачи завершены до allOf().thenRun()
):
pool-1-thread-1 waiting for 168
pool-1-thread-2 waiting for 292
main waiting for 941
pool-2-thread-1 waiting for 188
allOf() commpleted on main
Как управлять исполнителем, который будет использоваться после allOf()
(или anyOf()
)
Поскольку нет никакой гарантии, что исполнитель будет использоваться, за вызовом одного из этих методов должен следовать вызов *Async(, executor)
для управления тем, какой исполнитель будет использоваться.
Если вам нужно вернуть результат CompletableFuture
одного из этих вызовов, просто добавьте thenApplyAsync(i -> i, executor)
перед возвратом.
person
Didier L
schedule
22.04.2018
Executor asyncPool = useCommonPool ? ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
- person Andrew Tobilko   schedule 21.04.2018