docs: Update Async Method Invocation documentation

This commit is contained in:
Ilkka Seppälä
2024-03-12 21:59:17 +02:00
parent 02f1fdc261
commit a4a4edf990
2 changed files with 47 additions and 12 deletions
+39 -4
View File
@@ -3,7 +3,9 @@ title: Async Method Invocation
category: Concurrency category: Concurrency
language: en language: en
tag: tag:
- Asynchronous
- Reactive - Reactive
- Scalability
--- ---
## Intent ## 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 processing of multiple independent tasks and retrieving the results via
callbacks or waiting until everything is done. callbacks or waiting until everything is done.
## Also known as
* Asynchronous Procedure Call
## Explanation ## Explanation
Real world example Real world example
@@ -158,13 +164,42 @@ Here's the program console output.
Use the async method invocation pattern when Use the async method invocation pattern when
* You have multiple independent tasks that can run in parallel * When operations do not need to complete before proceeding with the next steps in a program.
* You need to improve the performance of a group of sequential tasks * 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.
* You have a limited amount of processing capacity or long-running tasks and the caller should not wait for the tasks to be ready * 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) * [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) * [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) * [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) * [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)
@@ -75,7 +75,7 @@ class ThreadAsyncExecutorTest {
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)}
*/ */
@Test @Test
void testSuccessfulTaskWithoutCallback() throws Exception { void testSuccessfulTaskWithoutCallback() {
assertTimeout(ofMillis(3000), () -> { assertTimeout(ofMillis(3000), () -> {
// Instantiate a new executor and start a new 'null' task ... // Instantiate a new executor and start a new 'null' task ...
final var executor = new ThreadAsyncExecutor(); final var executor = new ThreadAsyncExecutor();
@@ -101,7 +101,7 @@ class ThreadAsyncExecutorTest {
* AsyncCallback)} * AsyncCallback)}
*/ */
@Test @Test
void testSuccessfulTaskWithCallback() throws Exception { void testSuccessfulTaskWithCallback() {
assertTimeout(ofMillis(3000), () -> { assertTimeout(ofMillis(3000), () -> {
// Instantiate a new executor and start a new 'null' task ... // Instantiate a new executor and start a new 'null' task ...
final var executor = new ThreadAsyncExecutor(); final var executor = new ThreadAsyncExecutor();
@@ -134,7 +134,7 @@ class ThreadAsyncExecutorTest {
* task takes a while to execute * task takes a while to execute
*/ */
@Test @Test
void testLongRunningTaskWithoutCallback() throws Exception { void testLongRunningTaskWithoutCallback() {
assertTimeout(ofMillis(5000), () -> { assertTimeout(ofMillis(5000), () -> {
// Instantiate a new executor and start a new 'null' task ... // Instantiate a new executor and start a new 'null' task ...
final var executor = new ThreadAsyncExecutor(); final var executor = new ThreadAsyncExecutor();
@@ -174,7 +174,7 @@ class ThreadAsyncExecutorTest {
* AsyncCallback)} when a task takes a while to execute * AsyncCallback)} when a task takes a while to execute
*/ */
@Test @Test
void testLongRunningTaskWithCallback() throws Exception { void testLongRunningTaskWithCallback() {
assertTimeout(ofMillis(5000), () -> { assertTimeout(ofMillis(5000), () -> {
// Instantiate a new executor and start a new 'null' task ... // Instantiate a new executor and start a new 'null' task ...
final var executor = new ThreadAsyncExecutor(); final var executor = new ThreadAsyncExecutor();
@@ -222,7 +222,7 @@ class ThreadAsyncExecutorTest {
* ThreadAsyncExecutor#endProcess(AsyncResult)} * ThreadAsyncExecutor#endProcess(AsyncResult)}
*/ */
@Test @Test
void testEndProcess() throws Exception { void testEndProcess() {
assertTimeout(ofMillis(5000), () -> { assertTimeout(ofMillis(5000), () -> {
// Instantiate a new executor and start a new 'null' task ... // Instantiate a new executor and start a new 'null' task ...
final var executor = new ThreadAsyncExecutor(); final var executor = new ThreadAsyncExecutor();
@@ -259,7 +259,7 @@ class ThreadAsyncExecutorTest {
* the callable is 'null' * the callable is 'null'
*/ */
@Test @Test
void testNullTask() throws Exception { void testNullTask() {
assertTimeout(ofMillis(3000), () -> { assertTimeout(ofMillis(3000), () -> {
// Instantiate a new executor and start a new 'null' task ... // Instantiate a new executor and start a new 'null' task ...
final var executor = new ThreadAsyncExecutor(); final var executor = new ThreadAsyncExecutor();
@@ -286,7 +286,7 @@ class ThreadAsyncExecutorTest {
* AsyncCallback)} when the callable is 'null', but the asynchronous callback is provided * AsyncCallback)} when the callable is 'null', but the asynchronous callback is provided
*/ */
@Test @Test
void testNullTaskWithCallback() throws Exception { void testNullTaskWithCallback() {
assertTimeout(ofMillis(3000), () -> { assertTimeout(ofMillis(3000), () -> {
// Instantiate a new executor and start a new 'null' task ... // Instantiate a new executor and start a new 'null' task ...
final var executor = new ThreadAsyncExecutor(); final var executor = new ThreadAsyncExecutor();
@@ -322,7 +322,7 @@ class ThreadAsyncExecutorTest {
* AsyncCallback)} when both the callable and the asynchronous callback are 'null' * AsyncCallback)} when both the callable and the asynchronous callback are 'null'
*/ */
@Test @Test
void testNullTaskWithNullCallback() throws Exception { void testNullTaskWithNullCallback() {
assertTimeout(ofMillis(3000), () -> { assertTimeout(ofMillis(3000), () -> {
// Instantiate a new executor and start a new 'null' task ... // Instantiate a new executor and start a new 'null' task ...
final var executor = new ThreadAsyncExecutor(); final var executor = new ThreadAsyncExecutor();