* Fix languages * Missed change for version number * Add language field for presentation * Revert change in README for double buffer Co-authored-by: Jackie Nim <=>
174 lines
6.6 KiB
Markdown
174 lines
6.6 KiB
Markdown
---
|
|
layout: pattern
|
|
title: Async Method Invocation
|
|
folder: async-method-invocation
|
|
permalink: /patterns/async-method-invocation/
|
|
categories: Concurrency
|
|
language: en
|
|
tags:
|
|
- Reactive
|
|
---
|
|
|
|
## Intent
|
|
|
|
Asynchronous method invocation is a pattern where the calling thread
|
|
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.
|
|
|
|
## Explanation
|
|
|
|
Real world example
|
|
|
|
> Launching space rockets is an exciting business. The mission command gives an order to launch and
|
|
> after some undetermined time, the rocket either launches successfully or fails miserably.
|
|
|
|
In plain words
|
|
|
|
> Asynchronous method invocation starts task processing and returns immediately before the task is
|
|
> ready. The results of the task processing are returned to the caller later.
|
|
|
|
Wikipedia says
|
|
|
|
> In multithreaded computer programming, asynchronous method invocation (AMI), also known as
|
|
> asynchronous method calls or the asynchronous pattern is a design pattern in which the call site
|
|
> is not blocked while waiting for the called code to finish. Instead, the calling thread is
|
|
> notified when the reply arrives. Polling for a reply is an undesired option.
|
|
|
|
**Programmatic Example**
|
|
|
|
In this example, we are launching space rockets and deploying lunar rovers.
|
|
|
|
The application demonstrates the async method invocation pattern. The key parts of the pattern are
|
|
`AsyncResult` which is an intermediate container for an asynchronously evaluated value,
|
|
`AsyncCallback` which can be provided to be executed on task completion and `AsyncExecutor` that
|
|
manages the execution of the async tasks.
|
|
|
|
```java
|
|
public interface AsyncResult<T> {
|
|
boolean isCompleted();
|
|
T getValue() throws ExecutionException;
|
|
void await() throws InterruptedException;
|
|
}
|
|
```
|
|
|
|
```java
|
|
public interface AsyncCallback<T> {
|
|
void onComplete(T value, Optional<Exception> ex);
|
|
}
|
|
```
|
|
|
|
```java
|
|
public interface AsyncExecutor {
|
|
<T> AsyncResult<T> startProcess(Callable<T> task);
|
|
<T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback);
|
|
<T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException;
|
|
}
|
|
```
|
|
|
|
`ThreadAsyncExecutor` is an implementation of `AsyncExecutor`. Some of its key parts are highlighted
|
|
next.
|
|
|
|
```java
|
|
public class ThreadAsyncExecutor implements AsyncExecutor {
|
|
|
|
@Override
|
|
public <T> AsyncResult<T> startProcess(Callable<T> task) {
|
|
return startProcess(task, null);
|
|
}
|
|
|
|
@Override
|
|
public <T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback) {
|
|
var result = new CompletableResult<>(callback);
|
|
new Thread(
|
|
() -> {
|
|
try {
|
|
result.setValue(task.call());
|
|
} catch (Exception ex) {
|
|
result.setException(ex);
|
|
}
|
|
},
|
|
"executor-" + idx.incrementAndGet())
|
|
.start();
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public <T> T endProcess(AsyncResult<T> asyncResult)
|
|
throws ExecutionException, InterruptedException {
|
|
if (!asyncResult.isCompleted()) {
|
|
asyncResult.await();
|
|
}
|
|
return asyncResult.getValue();
|
|
}
|
|
}
|
|
```
|
|
|
|
Then we are ready to launch some rockets to see how everything works together.
|
|
|
|
```java
|
|
public static void main(String[] args) throws Exception {
|
|
// construct a new executor that will run async tasks
|
|
var executor = new ThreadAsyncExecutor();
|
|
|
|
// start few async tasks with varying processing times, two last with callback handlers
|
|
final var asyncResult1 = executor.startProcess(lazyval(10, 500));
|
|
final var asyncResult2 = executor.startProcess(lazyval("test", 300));
|
|
final var asyncResult3 = executor.startProcess(lazyval(50L, 700));
|
|
final var asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Deploying lunar rover"));
|
|
final var asyncResult5 =
|
|
executor.startProcess(lazyval("callback", 600), callback("Deploying lunar rover"));
|
|
|
|
// emulate processing in the current thread while async tasks are running in their own threads
|
|
Thread.sleep(350); // Oh boy, we are working hard here
|
|
log("Mission command is sipping coffee");
|
|
|
|
// wait for completion of the tasks
|
|
final var result1 = executor.endProcess(asyncResult1);
|
|
final var result2 = executor.endProcess(asyncResult2);
|
|
final var result3 = executor.endProcess(asyncResult3);
|
|
asyncResult4.await();
|
|
asyncResult5.await();
|
|
|
|
// log the results of the tasks, callbacks log immediately when complete
|
|
log("Space rocket <" + result1 + "> launch complete");
|
|
log("Space rocket <" + result2 + "> launch complete");
|
|
log("Space rocket <" + result3 + "> launch complete");
|
|
}
|
|
```
|
|
|
|
Here's the program console output.
|
|
|
|
```java
|
|
21:47:08.227 [executor-2] INFO com.iluwatar.async.method.invocation.App - Space rocket <test> launched successfully
|
|
21:47:08.269 [main] INFO com.iluwatar.async.method.invocation.App - Mission command is sipping coffee
|
|
21:47:08.318 [executor-4] INFO com.iluwatar.async.method.invocation.App - Space rocket <20> launched successfully
|
|
21:47:08.335 [executor-4] INFO com.iluwatar.async.method.invocation.App - Deploying lunar rover <20>
|
|
21:47:08.414 [executor-1] INFO com.iluwatar.async.method.invocation.App - Space rocket <10> launched successfully
|
|
21:47:08.519 [executor-5] INFO com.iluwatar.async.method.invocation.App - Space rocket <callback> launched successfully
|
|
21:47:08.519 [executor-5] INFO com.iluwatar.async.method.invocation.App - Deploying lunar rover <callback>
|
|
21:47:08.616 [executor-3] INFO com.iluwatar.async.method.invocation.App - Space rocket <50> launched successfully
|
|
21:47:08.617 [main] INFO com.iluwatar.async.method.invocation.App - Space rocket <10> launch complete
|
|
21:47:08.617 [main] INFO com.iluwatar.async.method.invocation.App - Space rocket <test> launch complete
|
|
21:47:08.618 [main] INFO com.iluwatar.async.method.invocation.App - Space rocket <50> launch complete
|
|
```
|
|
|
|
# Class diagram
|
|
|
|

|
|
|
|
## Applicability
|
|
|
|
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
|
|
|
|
## Real world examples
|
|
|
|
* [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)
|