Async-first thinking is essential for Web3 and exchange systems.
1. CompletableFuture (Advanced Usage & Pitfalls)
1.1 What It Is
- CompletableFuture\
: A future that you can complete explicitly (complete(value),completeExceptionally(throwable)) and compose with callbacks or further async stages. - Non-blocking: Use
thenApply,thenCompose,thenCombine,allOf,anyOf, etc., instead of blocking onget().
1.2 Creating a CompletableFuture
CompletableFuture.supplyAsync(Supplier)— run in ForkJoinPool.commonPool() (or provided Executor).CompletableFuture.runAsync(Runnable).new CompletableFuture<>()thencomplete(value)from another thread.
1.3 Chaining and Combining
- thenApply: transform result (sync function).
- thenCompose: chain another async operation (returns CompletableFuture).
- thenCombine / thenAcceptBoth: combine two futures.
- allOf(futures): one future that completes when all complete (result is void; gather results manually).
- anyOf(futures): completes with first result.
1.4 Pitfalls
- Blocking in async chain: Avoid
get()insidethenApply; usethenComposewith an async call instead. - Default executor: supplyAsync uses commonPool(); for many or blocking tasks, pass a dedicated Executor.
- Exception handling: use
handle,exceptionally, orwhenCompleteso failures don’t get lost. - Completing once: only the first
complete/completeExceptionallywins; later ones are ignored.
2. Asynchronous Error Handling
- exceptionally(Function): map throwable to a fallback value.
- handle(BiFunction): receive result or throwable; return new value (or throw).
- whenComplete(BiConsumer): side effect; doesn’t change result. Good for logging.
- CompletableFuture.completedExceptionally(throwable): start a chain in failed state.
Best practice: Ensure every async pipeline has a terminal handler (e.g. whenComplete or handle) so exceptions are not swallowed.
3. Backpressure Fundamentals
- Definition: When a fast producer feeds a slow consumer, the consumer signals “slow down” (backpressure) so the system doesn’t buffer unboundedly or OOM.
- Reactive Streams: Subscription with
request(n)— consumer requests n items; publisher sends at most n until next request. - In CompletableFuture: No built-in backpressure; you coordinate with bounded queues, semaphores, or reactive libraries (Reactor, RxJava) that implement backpressure.
4. Introduction to Reactive Streams Concepts
- Publisher: produces a stream of items (and completion/error).
- Subscriber: consumes with
onNext,onComplete,onError; signals demand viaSubscription.request(n). - Subscription: links publisher and subscriber;
request(n)is backpressure. - Processor: both subscriber and publisher (transform in the middle).
Java 9+ java.util.concurrent.Flow defines the interfaces; implementations (Reactor, RxJava) provide the machinery.
5. Hands-On
- Async order processing pipeline: stages: validate → enrich → persist → notify; each stage returns CompletableFuture; chain with thenCompose; handle errors.
- Sync vs async latency benchmarking: same workload done synchronously vs async (e.g. multiple I/O calls); measure end-to-end latency and throughput.
- Failure propagation in async flows: one stage fails; ensure exception propagates and alternative path (e.g. retry or fallback) is clear.
6. Code in This Module
| Class | Purpose |
|---|---|
CompletableFutureAdvanced |
Chaining, thenCompose, thenCombine, allOf; exception handling. |
AsyncErrorHandling |
exceptionally, handle, whenComplete; propagation. |
AsyncOrderPipeline |
Validate → enrich → persist → notify with CF; error handling. |
SyncVsAsyncLatency |
Benchmark: N I/O-like calls in sequence vs in parallel with CF. |