The library provides an implementation of the CompletionStage interface and related classes these are designed to support long-running blocking tasks (typically, I/O bound). This functionality augments the sole Java 8 built-in implementation, CompletableFuture, that is primarily supports computational tasks. Also, the library helps with numerous asynchronous programing challenges like handling timeouts, retry/poll functionality, orchestrating results of multiple concurrent computations and similar.
Since the version 0.7.0 the library is shipped as a multi-release JAR and may be used both with Java 8 as a classpath library or with Java 9+ as a module.
IMPORTANT!
In the version 0.8.0 the artifact was renamed: New name:
<dependency>
<groupId>net.tascalate</groupId>
<artifactId>net.tascalate.concurrent</artifactId>
<version>1.0.1</version>
</dependency>Old Name
<dependency>
<groupId>net.tascalate.concurrent</groupId>
<artifactId>net.tascalate.concurrent.lib</artifactId>
<version>0.7.1</version>
</dependency>There are several shortcomings in the CompletableFuture implementation that complicate its use in real-world asynchronous programming, especially when dealing with I/O-bound, interruptible tasks:
- Lack of true interruptibility: The
CompletableFuture.cancel()method does not interrupt the underlying thread; it merely transitions the future to an exceptionally completed state. Consequently, even if you use blocking calls inside functions passed tothenApplyAsync,thenAcceptAsync, etc., these functions will run to completion and cannot be interrupted. For more details, see CompletableFuture can't be interrupted by Tomasz Nurkiewicz. - Unconfigurable default executor: By default, all
*Asynccomposition methods ofCompletableFutureuse theForkJoinPool.commonPool()(see here) unless an explicit Executor is specified. This thread pool is shared among all CompletableFuture instances and parallel streams across all applications deployed on the same JVM. This hard-coded, unconfigurable thread pool is completely outside the application developer's control, making it difficult to monitor and scale. Therefore, in robust real-life applications, you should always specify your ownExecutor. While Java 9+ introduced API enhancements to mitigate this, addressing it still requires custom coding. - Inconvenient composition API: The built-in Java concurrency API provides a rather limited way to combine multiple CompletionStages. The
CompletableFuture.allOfandCompletableFuture.anyOfmethods accept only CompletableFuture arguments, meaning there is no built-in mechanism to combine arbitrary CompletionStages without first converting them to CompletableFuture. Furthermore,CompletableFuture.allOfreturns aCompletableFuture<Void>, making it inconvenient to extract the individual results of each supplied future.CompletableFuture.anyOfis even more restrictive in this regard; for more details, please read CompletableFuture in Action (see "Shortcomings") by Tomasz Nurkiewicz. - Late addition of timeouts and delays: Support for timeouts and delays was introduced to CompletableFuture only in Java 9, leaving widely used Java 8 applications without this crucial functionality. Furthermore, certain design decisions, such as relying on delayed executors instead of a dedicated
delayoperator, are somewhat questionable.
While numerous open-source libraries address some of these shortcomings, none provide a fully interruptible CompletionStage implementation, and none solve all of these issues coherently.
To use a library you have to add a single Maven dependency
<dependency>
<groupId>net.tascalate</groupId>
<artifactId>net.tascalate.concurrent</artifactId>
<version>1.0.1</version>
</dependency>Complete user documentation is available HERE
Internal implementation details are greatly inspired by the work done by Lukáš Křečan. The part of the polling / asynchronous retry functionality is adopted from the async-retry library by Tomasz Nurkiewicz