From a4a4edf99063cfcf0c68cc0028b810ebe707f7a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Tue, 12 Mar 2024 21:59:17 +0200 Subject: [PATCH] docs: Update Async Method Invocation documentation --- async-method-invocation/README.md | 43 +++++++++++++++++-- .../invocation/ThreadAsyncExecutorTest.java | 16 +++---- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/async-method-invocation/README.md b/async-method-invocation/README.md index 9071b566e..a032a0e6f 100644 --- a/async-method-invocation/README.md +++ b/async-method-invocation/README.md @@ -3,7 +3,9 @@ title: Async Method Invocation category: Concurrency language: en tag: + - Asynchronous - Reactive + - Scalability --- ## Intent @@ -13,6 +15,10 @@ is not blocked while waiting results of tasks. The pattern provides parallel processing of multiple independent tasks and retrieving the results via callbacks or waiting until everything is done. +## Also known as + +* Asynchronous Procedure Call + ## Explanation Real world example @@ -158,13 +164,42 @@ Here's the program console output. Use the async method invocation pattern when -* You have multiple independent tasks that can run in parallel -* You need to improve the performance of a group of sequential tasks -* You have a limited amount of processing capacity or long-running tasks and the caller should not wait for the tasks to be ready +* When operations do not need to complete before proceeding with the next steps in a program. +* For tasks that are resource-intensive or time-consuming, such as IO operations, network requests, or complex computations, where making the operation synchronous would significantly impact performance or user experience. +* In GUI applications to prevent freezing or unresponsiveness during long-running tasks. +* In web applications for non-blocking IO operations. -## Real world examples +## Known Uses +* Web servers handling HTTP requests asynchronously to improve throughput and reduce latency. +* Desktop and mobile applications using background threads to perform time-consuming operations without blocking the user interface. +* Microservices architectures where services perform asynchronous communications via messaging queues or event streams. * [FutureTask](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/FutureTask.html) * [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) * [ExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html) * [Task-based Asynchronous Pattern](https://msdn.microsoft.com/en-us/library/hh873175.aspx) + +## Consequences + +Benefits: + +* Improved Responsiveness: The main thread or application flow remains unblocked, improving the user experience in GUI applications and overall responsiveness. +* Better Resource Utilization: By enabling parallel execution, system resources (like CPU and IO) are utilized more efficiently, potentially improving the application's throughput. +* Scalability: Easier to scale applications, as tasks can be distributed across available resources more effectively. + +Trade-offs: + +* Complexity: Introducing asynchronous operations can complicate the codebase, making it more challenging to understand, debug, and maintain. +* Resource Management: Requires careful management of threads or execution contexts, which can introduce overhead and potential resource exhaustion issues. +* Error Handling: Asynchronous operations can make error handling more complex, as exceptions may occur in different threads or at different times. + +Related Patterns: + +* [Command](https://java-design-patterns.com/patterns/command/): Asynchronous method invocation can be used to implement the Command pattern, where commands are executed asynchronously. +* [Observer](https://java-design-patterns.com/patterns/observer/): Asynchronous method invocation can be used to notify observers asynchronously when a subject's state changes. +* [Promise](https://java-design-patterns.com/patterns/promise/): The AsyncResult interface can be considered a form of Promise, representing a value that may not be available yet. + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3Ti1N4f) +* [Java Concurrency in Practice](https://amzn.to/4ab97VU) diff --git a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java index 4698a092c..7af9074a4 100644 --- a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java +++ b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java @@ -75,7 +75,7 @@ class ThreadAsyncExecutorTest { * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} */ @Test - void testSuccessfulTaskWithoutCallback() throws Exception { + void testSuccessfulTaskWithoutCallback() { assertTimeout(ofMillis(3000), () -> { // Instantiate a new executor and start a new 'null' task ... final var executor = new ThreadAsyncExecutor(); @@ -101,7 +101,7 @@ class ThreadAsyncExecutorTest { * AsyncCallback)} */ @Test - void testSuccessfulTaskWithCallback() throws Exception { + void testSuccessfulTaskWithCallback() { assertTimeout(ofMillis(3000), () -> { // Instantiate a new executor and start a new 'null' task ... final var executor = new ThreadAsyncExecutor(); @@ -134,7 +134,7 @@ class ThreadAsyncExecutorTest { * task takes a while to execute */ @Test - void testLongRunningTaskWithoutCallback() throws Exception { + void testLongRunningTaskWithoutCallback() { assertTimeout(ofMillis(5000), () -> { // Instantiate a new executor and start a new 'null' task ... final var executor = new ThreadAsyncExecutor(); @@ -174,7 +174,7 @@ class ThreadAsyncExecutorTest { * AsyncCallback)} when a task takes a while to execute */ @Test - void testLongRunningTaskWithCallback() throws Exception { + void testLongRunningTaskWithCallback() { assertTimeout(ofMillis(5000), () -> { // Instantiate a new executor and start a new 'null' task ... final var executor = new ThreadAsyncExecutor(); @@ -222,7 +222,7 @@ class ThreadAsyncExecutorTest { * ThreadAsyncExecutor#endProcess(AsyncResult)} */ @Test - void testEndProcess() throws Exception { + void testEndProcess() { assertTimeout(ofMillis(5000), () -> { // Instantiate a new executor and start a new 'null' task ... final var executor = new ThreadAsyncExecutor(); @@ -259,7 +259,7 @@ class ThreadAsyncExecutorTest { * the callable is 'null' */ @Test - void testNullTask() throws Exception { + void testNullTask() { assertTimeout(ofMillis(3000), () -> { // Instantiate a new executor and start a new 'null' task ... final var executor = new ThreadAsyncExecutor(); @@ -286,7 +286,7 @@ class ThreadAsyncExecutorTest { * AsyncCallback)} when the callable is 'null', but the asynchronous callback is provided */ @Test - void testNullTaskWithCallback() throws Exception { + void testNullTaskWithCallback() { assertTimeout(ofMillis(3000), () -> { // Instantiate a new executor and start a new 'null' task ... final var executor = new ThreadAsyncExecutor(); @@ -322,7 +322,7 @@ class ThreadAsyncExecutorTest { * AsyncCallback)} when both the callable and the asynchronous callback are 'null' */ @Test - void testNullTaskWithNullCallback() throws Exception { + void testNullTaskWithNullCallback() { assertTimeout(ofMillis(3000), () -> { // Instantiate a new executor and start a new 'null' task ... final var executor = new ThreadAsyncExecutor();