public class BigFTest {
public static void main(String[] args) throws Throwable {
final long sleepMillis = 1500;
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(() -> { // bigF
Try.run(() -> Thread.sleep(sleepMillis));
Option.of(-10).toTry().flatMap(t -> Try.run(() -> foo(t))).get();
});
Future.run(() -> Thread.sleep(sleepMillis)).onComplete(ignored -> System.out.println("Slang future completed " + ignored)); // #1
executorService.shutdown();
System.out.println("Normal termination? " + executorService.awaitTermination(2, TimeUnit.SECONDS));
}
private static void foo(Integer t) {
}
}
Output:
Normal termination? true
Slang future completed Success(null)
Observation:
The application quits after 60 seconds. This is hard-baked behavior of Java's Executors.newCachedThreadPool()
, which is Javaslang's default ExecutorService. In particular it is used in line #1 where we not pass a specific executor service: Future.run(() -> ...)
.
Output:
Normal termination? true
Observation:
As expected, the application quits immediately because we do not execute Future.run(() -> ...)
anymore, which would use the default ExecutorService under the hood, that waits for 60 seconds.
However, it can be fixed by shutting down the Javaslang's default ExecutorService:
Future.DEFAULT_EXECUTOR_SERVICE.shutdown();
System.out.println("Normal termination? " + Future.DEFAULT_EXECUTOR_SERVICE.awaitTermination(2, TimeUnit.SECONDS));
Future.run(executorService, () -> Thread.sleep(sleepMillis)).onComplete(ignored -> System.out.println("Slang future completed " + ignored)); // #1
Output:
Normal termination? true
Observation:
We are missing the output
Slang future completed Success(null)
This is because we have a fixed thread-pool having only one available thread. In our execution the first Thread
executorService.submit(() -> { // bigF
...
});
still runs when we call executorService.shutdown()
. Especially the Future.run(...)
is rejected by the ExecutorService because there is no Thread available.
Question: Does it help to increase the fixedThreadPool size in order to run Future.run(...)
?
ExecutorService executorService = Executors.newFixedThreadPool(2);
...
Future.run(executorService, () -> Thread.sleep(sleepMillis)).onComplete(ignored -> System.out.println("Slang future completed " + ignored)); // #1
Output:
Normal termination? true
Observation:
We are still missing the output Slang future completed Success(null)
, what is going on here?
The answer is that Future.run()
first runs Thread.sleep()
, in the meanwhile the main thread calls executorService.shutdown()
.
When it comes to the onComplete()
call that should outputs Slang future completed Success(null)
the Future's current behavior is to start a new Thread for that output. But that is not possible, because the ExecutorService
is already shutdown and rejects new Threads.
Scala's behavior is the same, but we want to change that in Javaslang for release 2.1.0. The onComplete() handler should be executed on the same Thread the Future has been run. More details here: see #1530
We want to go even one step further and let the user decide if new threads should be created for subsequent operations: see #1537
Note:
In order to verify our observation we could force the main thread to wait longer than the Future.run()
needs to complete:
Try.run(() -> Thread.sleep(sleepMillis * 3));
executorService.shutdown();
Then the executorService is still able to run the onComplete() handler on a new thread. Output:
Slang future completed Success(null)
Normal termination? true
I hope this helps to understand the behavior of Future composition regarding new Thread creation and internal usage of an ExecutorService.
Cheers
- Daniel