CompletableFuture.defaultExecutor()
Ben Manes
ben.manes at gmail.com
Fri May 20 15:48:09 UTC 2022
Thank you. I agree with those priorities. I do strongly recommend switching
this default executor after this JEP is delivered.
For those less familiar, please see the test code below. This shows how
easy it is to misuse CompletableFuture and that virtual threads resolve
this dilemma.
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public final class CompletableFutureTest {
public static void run(/* @Nullable */ Executor executor) throws
Exception {
long start = System.nanoTime();
// A blocking task that waits for a shutdown signal
var latch = new CountDownLatch(1);
Runnable task = () -> {
try {
latch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
};
// Create enough tasks to exhaust platform threads and compensations
for managed blockers
int tasks = 2 * Runtime.getRuntime().availableProcessors();
var futures = new ArrayList<CompletableFuture<?>>();
for (int i = 0; i < tasks; i++) {
runAsync(task, executor);
}
// Wait for a no-op task to see if work can proceed
runAsync(() -> {}, executor).get(3, TimeUnit.SECONDS);
// Clean up
latch.countDown();
CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join();
long end = System.nanoTime();
System.out.printf("Ran in %,d ns%n", (end - start));
}
private static CompletableFuture<Void> runAsync(Runnable task, Executor
executor) {
return (executor == null)
? CompletableFuture.runAsync(task)
: CompletableFuture.runAsync(task, executor);
}
public static void main(String[] args) throws Exception {
System.out.println("Executor: Virtual");
run(Executors.newVirtualThreadPerTaskExecutor());
System.out.println("%nExecutor: default (none specified)");
run(/* executor */ null);
}
}
Executor: Virtual
Ran in 56,293,548 ns
Executor: default (none specified)
Exception in thread "main" java.util.concurrent.TimeoutException
at
java.base/java.util.concurrent.CompletableFuture.timedGet(CompletableFuture.java:1960)
at
java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2095)
at CompletableFutureTest.run(CompletableFutureTest.java:49)
at CompletableFutureTest.main(CompletableFutureTest.java:70)
On Fri, May 20, 2022 at 12:47 AM Alan Bateman <Alan.Bateman at oracle.com>
wrote:
> On 20/05/2022 01:58, Ben Manes wrote:
> > Hi August,
> >
> > I am asking in the context of CompletableFuture.runAsync(r) and friends.
> As
> > this defaults to using platform threads it can quickly exhaust the FJP
> > (e.g. futures blocking on other futures). A common footgun would be
> removed
> > if the default was changed to use virtual threads instead. I have seen
> this
> > surprise developers both in the context of Caffeine (AsyncCache) and
> > work-related projects. I suspect this change is the team's plan, but I
> have
> > not seen it discussed explicitly before. I do hope to switch Caffeine's
> > default executor setting when appropriate, since cache loads are
> > commonly I/O operations.
> This project is focused right now on code written in the synchronous
> style. So no plans at this time to touch the FJ common pool. Code using
> the common pool (CompletableFuture and others) can of course using
> FJP.ManagedBlocker when doing blocking operations. One area of future
> investigation may be a concurrent equivalent to parallel streams that
> uses virtual threads but this is not for the JEPs that are currently in
> progress.
>
> -Alan
>
More information about the loom-dev
mailing list