Compare commits
2 Commits
async-meth
...
all-contri
Author | SHA1 | Date | |
---|---|---|---|
407c13e813 | |||
ba24586b49 |
@ -1431,6 +1431,15 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"code"
|
"code"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "qfxl",
|
||||||
|
"name": "yonghong Xu",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/14086462?v=4",
|
||||||
|
"profile": "https://xuyonghong.cn/",
|
||||||
|
"contributions": [
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 4,
|
"contributorsPerLine": 4,
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
[](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
|
[](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
|
||||||
[](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||||
[](#contributors-)
|
[](#contributors-)
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
@ -311,6 +311,7 @@ This project is licensed under the terms of the MIT license.
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center"><a href="https://github.com/noamgrinch"><img src="https://avatars.githubusercontent.com/u/31648669?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Noam Greenshtain</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=noamgrinch" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/noamgrinch"><img src="https://avatars.githubusercontent.com/u/31648669?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Noam Greenshtain</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=noamgrinch" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://xuyonghong.cn/"><img src="https://avatars.githubusercontent.com/u/14086462?v=4?s=100" width="100px;" alt=""/><br /><sub><b>yonghong Xu</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=qfxl" title="Documentation">📖</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
@ -9,160 +9,21 @@ tags:
|
|||||||
---
|
---
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
|
Asynchronous method invocation is pattern where the calling thread
|
||||||
Asynchronous method invocation is a pattern where the calling thread
|
|
||||||
is not blocked while waiting results of tasks. The pattern provides parallel
|
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.
|
||||||
|
|
||||||
## 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
|
# Class diagram
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Applicability
|
## Applicability
|
||||||
|
Use async method invocation pattern when
|
||||||
Use the async method invocation pattern when
|
|
||||||
|
|
||||||
* You have multiple independent tasks that can run in parallel
|
* You have multiple independent tasks that can run in parallel
|
||||||
* You need to improve the performance of a group of sequential tasks
|
* 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
|
* You have limited amount of processing capacity or long running tasks and the
|
||||||
|
caller should not wait the tasks to be ready
|
||||||
|
|
||||||
## Real world examples
|
## Real world examples
|
||||||
|
|
||||||
|
@ -27,12 +27,10 @@ import java.util.concurrent.Callable;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In this example, we are launching space rockets and deploying lunar rovers.
|
* This application demonstrates the async method invocation pattern. Key parts of the pattern are
|
||||||
*
|
* <code>AsyncResult</code> which is an intermediate container for an asynchronously evaluated
|
||||||
* <p>The application demonstrates the async method invocation pattern. The key parts of the
|
* value, <code>AsyncCallback</code> which can be provided to be executed on task completion and
|
||||||
* pattern are <code>AsyncResult</code> which is an intermediate container for an asynchronously
|
* <code>AsyncExecutor</code> that manages the execution of the async tasks.
|
||||||
* evaluated value, <code>AsyncCallback</code> which can be provided to be executed on task
|
|
||||||
* completion and <code>AsyncExecutor</code> that manages the execution of the async tasks.
|
|
||||||
*
|
*
|
||||||
* <p>The main method shows example flow of async invocations. The main thread starts multiple
|
* <p>The main method shows example flow of async invocations. The main thread starts multiple
|
||||||
* tasks with variable durations and then continues its own work. When the main thread has done it's
|
* tasks with variable durations and then continues its own work. When the main thread has done it's
|
||||||
@ -70,14 +68,13 @@ public class App {
|
|||||||
final var asyncResult1 = executor.startProcess(lazyval(10, 500));
|
final var asyncResult1 = executor.startProcess(lazyval(10, 500));
|
||||||
final var asyncResult2 = executor.startProcess(lazyval("test", 300));
|
final var asyncResult2 = executor.startProcess(lazyval("test", 300));
|
||||||
final var asyncResult3 = executor.startProcess(lazyval(50L, 700));
|
final var asyncResult3 = executor.startProcess(lazyval(50L, 700));
|
||||||
final var asyncResult4 = executor.startProcess(lazyval(20, 400),
|
final var asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Callback result 4"));
|
||||||
callback("Deploying lunar rover"));
|
|
||||||
final var asyncResult5 =
|
final var asyncResult5 =
|
||||||
executor.startProcess(lazyval("callback", 600), callback("Deploying lunar rover"));
|
executor.startProcess(lazyval("callback", 600), callback("Callback result 5"));
|
||||||
|
|
||||||
// emulate processing in the current thread while async tasks are running in their own threads
|
// 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
|
Thread.sleep(350); // Oh boy I'm working hard here
|
||||||
log("Mission command is sipping coffee");
|
log("Some hard work done");
|
||||||
|
|
||||||
// wait for completion of the tasks
|
// wait for completion of the tasks
|
||||||
final var result1 = executor.endProcess(asyncResult1);
|
final var result1 = executor.endProcess(asyncResult1);
|
||||||
@ -87,9 +84,9 @@ public class App {
|
|||||||
asyncResult5.await();
|
asyncResult5.await();
|
||||||
|
|
||||||
// log the results of the tasks, callbacks log immediately when complete
|
// log the results of the tasks, callbacks log immediately when complete
|
||||||
log("Space rocket <" + result1 + "> launch complete");
|
log("Result 1: " + result1);
|
||||||
log("Space rocket <" + result2 + "> launch complete");
|
log("Result 2: " + result2);
|
||||||
log("Space rocket <" + result3 + "> launch complete");
|
log("Result 3: " + result3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -102,7 +99,7 @@ public class App {
|
|||||||
private static <T> Callable<T> lazyval(T value, long delayMillis) {
|
private static <T> Callable<T> lazyval(T value, long delayMillis) {
|
||||||
return () -> {
|
return () -> {
|
||||||
Thread.sleep(delayMillis);
|
Thread.sleep(delayMillis);
|
||||||
log("Space rocket <" + value + "> launched successfully");
|
log("Task completed with: " + value);
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -118,7 +115,7 @@ public class App {
|
|||||||
if (ex.isPresent()) {
|
if (ex.isPresent()) {
|
||||||
log(name + " failed: " + ex.map(Exception::getMessage).orElse(""));
|
log(name + " failed: " + ex.map(Exception::getMessage).orElse(""));
|
||||||
} else {
|
} else {
|
||||||
log(name + " <" + value + ">");
|
log(name + ": " + value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user