Compare commits
9 Commits
all-contri
...
balking-ex
Author | SHA1 | Date | |
---|---|---|---|
c2126bef76 | |||
468cb1ec27 | |||
b5ed8b2278 | |||
8fc64a2d38 | |||
7e91322a43 | |||
e9106ccfc5 | |||
77ffae5ecc | |||
74abc7a0d6 | |||
ccf350b611 |
@ -1422,6 +1422,24 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"code"
|
"code"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "noamgrinch",
|
||||||
|
"name": "Noam Greenshtain",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/31648669?v=4",
|
||||||
|
"profile": "https://github.com/noamgrinch",
|
||||||
|
"contributions": [
|
||||||
|
"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,
|
||||||
|
2
.github/workflows/maven-pr-builder.yml
vendored
2
.github/workflows/maven-pr-builder.yml
vendored
@ -29,7 +29,7 @@ name: Java PR Builder
|
|||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
types: [ opened, reopened, synchronize ]
|
types: [ opened, reopened, synchronize, labeled, unlabeled ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
@ -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/>
|
||||||
@ -309,6 +309,10 @@ This project is licensed under the terms of the MIT license.
|
|||||||
<td align="center"><a href="https://github.com/richardmr36"><img src="https://avatars.githubusercontent.com/u/19147333?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martel Richard</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=richardmr36" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/richardmr36"><img src="https://avatars.githubusercontent.com/u/19147333?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martel Richard</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=richardmr36" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/va1m"><img src="https://avatars.githubusercontent.com/u/17025445?v=4?s=100" width="100px;" alt=""/><br /><sub><b>va1m</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=va1m" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/va1m"><img src="https://avatars.githubusercontent.com/u/17025445?v=4?s=100" width="100px;" alt=""/><br /><sub><b>va1m</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=va1m" title="Code">💻</a></td>
|
||||||
</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://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>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- markdownlint-restore -->
|
<!-- markdownlint-restore -->
|
||||||
|
@ -79,12 +79,12 @@ public abstract class ActiveCreature{
|
|||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
We can see that any class that will extend the ActiveCreature class will have its own thread of control to execute and invocate methods.
|
We can see that any class that will extend the ActiveCreature class will have its own thread of control to execute and invocate methods.
|
||||||
|
|
||||||
For example, the Orc class:
|
For example, the Orc class:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class Orc extends ActiveCreature {
|
public class Orc extends ActiveCreature {
|
||||||
|
|
||||||
@ -96,6 +96,7 @@ public class Orc extends ActiveCreature {
|
|||||||
```
|
```
|
||||||
|
|
||||||
Now, we can create multiple creatures such as Orcs, tell them to eat and roam and they will execute it on their own thread of control:
|
Now, we can create multiple creatures such as Orcs, tell them to eat and roam and they will execute it on their own thread of control:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
var app = new App();
|
var app = new App();
|
||||||
@ -121,4 +122,4 @@ Now, we can create multiple creatures such as Orcs, tell them to eat and roam an
|
|||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||
|
|
||||||

|

|
||||||
|
@ -1,3 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.iluwatar.activeobject;
|
package com.iluwatar.activeobject;
|
||||||
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
@ -1,3 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.iluwatar.activeobject;
|
package com.iluwatar.activeobject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,3 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.iluwatar.activeobject;
|
package com.iluwatar.activeobject;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
@ -1,3 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.iluwatar.activeobject;
|
package com.iluwatar.activeobject;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
@ -9,21 +9,160 @@ 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 limited amount of processing capacity or long running tasks and the
|
* You have a limited amount of processing capacity or long-running tasks and the caller should not wait for the tasks to be ready
|
||||||
caller should not wait the tasks to be ready
|
|
||||||
|
|
||||||
## Real world examples
|
## Real world examples
|
||||||
|
|
||||||
|
@ -27,10 +27,12 @@ import java.util.concurrent.Callable;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This application demonstrates the async method invocation pattern. Key parts of the pattern are
|
* In this example, we are launching space rockets and deploying lunar rovers.
|
||||||
* <code>AsyncResult</code> which is an intermediate container for an asynchronously evaluated
|
*
|
||||||
* value, <code>AsyncCallback</code> which can be provided to be executed on task completion and
|
* <p>The application demonstrates the async method invocation pattern. The key parts of the
|
||||||
* <code>AsyncExecutor</code> that manages the execution of the async tasks.
|
* pattern are <code>AsyncResult</code> which is an intermediate container for an asynchronously
|
||||||
|
* 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
|
||||||
@ -68,13 +70,14 @@ 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), callback("Callback result 4"));
|
final var asyncResult4 = executor.startProcess(lazyval(20, 400),
|
||||||
|
callback("Deploying lunar rover"));
|
||||||
final var asyncResult5 =
|
final var asyncResult5 =
|
||||||
executor.startProcess(lazyval("callback", 600), callback("Callback result 5"));
|
executor.startProcess(lazyval("callback", 600), callback("Deploying lunar rover"));
|
||||||
|
|
||||||
// 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 I'm working hard here
|
Thread.sleep(350); // Oh boy, we are working hard here
|
||||||
log("Some hard work done");
|
log("Mission command is sipping coffee");
|
||||||
|
|
||||||
// wait for completion of the tasks
|
// wait for completion of the tasks
|
||||||
final var result1 = executor.endProcess(asyncResult1);
|
final var result1 = executor.endProcess(asyncResult1);
|
||||||
@ -84,9 +87,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("Result 1: " + result1);
|
log("Space rocket <" + result1 + "> launch complete");
|
||||||
log("Result 2: " + result2);
|
log("Space rocket <" + result2 + "> launch complete");
|
||||||
log("Result 3: " + result3);
|
log("Space rocket <" + result3 + "> launch complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -99,7 +102,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("Task completed with: " + value);
|
log("Space rocket <" + value + "> launched successfully");
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -115,7 +118,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 + ">");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -9,19 +9,131 @@ tags:
|
|||||||
---
|
---
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
Balking Pattern is used to prevent an object from executing certain code if it is an
|
|
||||||
incomplete or inappropriate state
|
Balking Pattern is used to prevent an object from executing a certain code if it is in an incomplete
|
||||||
|
or inappropriate state.
|
||||||
|
|
||||||
|
## Explanation
|
||||||
|
|
||||||
|
Real world example
|
||||||
|
|
||||||
|
> There's a start-button in a washing machine to initiate the laundry washing. When the washing
|
||||||
|
> machine is inactive the button works as expected, but if it's already washing the button does
|
||||||
|
> nothing.
|
||||||
|
|
||||||
|
In plain words
|
||||||
|
|
||||||
|
> Using the balking pattern, a certain code executes only if the object is in particular state.
|
||||||
|
|
||||||
|
Wikipedia says
|
||||||
|
|
||||||
|
> The balking pattern is a software design pattern that only executes an action on an object when
|
||||||
|
> the object is in a particular state. For example, if an object reads ZIP files and a calling
|
||||||
|
> method invokes a get method on the object when the ZIP file is not open, the object would "balk"
|
||||||
|
> at the request.
|
||||||
|
|
||||||
|
**Programmatic Example**
|
||||||
|
|
||||||
|
In this example implementation, `WashingMachine` is an object that has two states in which it can
|
||||||
|
be: ENABLED and WASHING. If the machine is ENABLED, the state changes to WASHING using a thread-safe
|
||||||
|
method. On the other hand, if it already has been washing and any other thread executes `wash()`
|
||||||
|
it won't do that and returns without doing anything.
|
||||||
|
|
||||||
|
Here are the relevant parts of the `WashingMachine` class.
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Slf4j
|
||||||
|
public class WashingMachine {
|
||||||
|
|
||||||
|
private final DelayProvider delayProvider;
|
||||||
|
private WashingMachineState washingMachineState;
|
||||||
|
|
||||||
|
public WashingMachine(DelayProvider delayProvider) {
|
||||||
|
this.delayProvider = delayProvider;
|
||||||
|
this.washingMachineState = WashingMachineState.ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WashingMachineState getWashingMachineState() {
|
||||||
|
return washingMachineState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void wash() {
|
||||||
|
synchronized (this) {
|
||||||
|
var machineState = getWashingMachineState();
|
||||||
|
LOGGER.info("{}: Actual machine state: {}", Thread.currentThread().getName(), machineState);
|
||||||
|
if (this.washingMachineState == WashingMachineState.WASHING) {
|
||||||
|
LOGGER.error("Cannot wash if the machine has been already washing!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.washingMachineState = WashingMachineState.WASHING;
|
||||||
|
}
|
||||||
|
LOGGER.info("{}: Doing the washing", Thread.currentThread().getName());
|
||||||
|
this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void endOfWashing() {
|
||||||
|
washingMachineState = WashingMachineState.ENABLED;
|
||||||
|
LOGGER.info("{}: Washing completed.", Thread.currentThread().getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's the simple `DelayProvider` interface used by the `WashingMachine`.
|
||||||
|
|
||||||
|
```java
|
||||||
|
public interface DelayProvider {
|
||||||
|
void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now we introduce the application using the `WashingMachine`.
|
||||||
|
|
||||||
|
```java
|
||||||
|
public static void main(String... args) {
|
||||||
|
final var washingMachine = new WashingMachine();
|
||||||
|
var executorService = Executors.newFixedThreadPool(3);
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
executorService.execute(washingMachine::wash);
|
||||||
|
}
|
||||||
|
executorService.shutdown();
|
||||||
|
try {
|
||||||
|
executorService.awaitTermination(10, TimeUnit.SECONDS);
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
LOGGER.error("ERROR: Waiting on executor service shutdown!");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is the console output of the program.
|
||||||
|
|
||||||
|
```
|
||||||
|
14:02:52.268 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-2: Actual machine state: ENABLED
|
||||||
|
14:02:52.272 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-2: Doing the washing
|
||||||
|
14:02:52.272 [pool-1-thread-3] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-3: Actual machine state: WASHING
|
||||||
|
14:02:52.273 [pool-1-thread-3] ERROR com.iluwatar.balking.WashingMachine - Cannot wash if the machine has been already washing!
|
||||||
|
14:02:52.273 [pool-1-thread-1] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-1: Actual machine state: WASHING
|
||||||
|
14:02:52.273 [pool-1-thread-1] ERROR com.iluwatar.balking.WashingMachine - Cannot wash if the machine has been already washing!
|
||||||
|
14:02:52.324 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - 14: Washing completed.
|
||||||
|
```
|
||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Applicability
|
## Applicability
|
||||||
|
|
||||||
Use the Balking pattern when
|
Use the Balking pattern when
|
||||||
|
|
||||||
* you want to invoke an action on an object only when it is in a particular state
|
* You want to invoke an action on an object only when it is in a particular state
|
||||||
* objects are generally only in a state that is prone to balking temporarily
|
* Objects are generally only in a state that is prone to balking temporarily but for an unknown
|
||||||
but for an unknown amount of time
|
amount of time
|
||||||
|
|
||||||
## Related patterns
|
## Related patterns
|
||||||
* Guarded Suspension Pattern
|
|
||||||
* Double Checked Locking Pattern
|
* [Guarded Suspension Pattern](https://java-design-patterns.com/patterns/guarded-suspension/)
|
||||||
|
* [Double Checked Locking Pattern](https://java-design-patterns.com/patterns/double-checked-locking/)
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
* [Patterns in Java: A Catalog of Reusable Design Patterns Illustrated with UML, 2nd Edition, Volume 1](https://www.amazon.com/gp/product/0471227293/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0471227293&linkId=0e39a59ffaab93fb476036fecb637b99)
|
||||||
|
@ -32,11 +32,11 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
* then the method will return without doing anything. Objects that use this pattern are generally
|
* then the method will return without doing anything. Objects that use this pattern are generally
|
||||||
* only in a state that is prone to balking temporarily but for an unknown amount of time
|
* only in a state that is prone to balking temporarily but for an unknown amount of time
|
||||||
*
|
*
|
||||||
* <p>In this example implementation WashingMachine is an object that has two states in which it
|
* <p>In this example implementation, {@link WashingMachine} is an object that has two states in
|
||||||
* can be: ENABLED and WASHING. If the machine is ENABLED the state is changed into WASHING that any
|
* which it can be: ENABLED and WASHING. If the machine is ENABLED, the state changes to WASHING
|
||||||
* other thread can't invoke this action on this and then do the job. On the other hand if it have
|
* using a thread-safe method. On the other hand, if it already has been washing and any other
|
||||||
* been already washing and any other thread execute wash() it can't do that once again and returns
|
* thread executes {@link WashingMachine#wash()} it won't do that and returns without doing
|
||||||
* doing nothing.
|
* anything.
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class App {
|
public class App {
|
||||||
@ -54,11 +54,12 @@ public class App {
|
|||||||
}
|
}
|
||||||
executorService.shutdown();
|
executorService.shutdown();
|
||||||
try {
|
try {
|
||||||
executorService.awaitTermination(10, TimeUnit.SECONDS);
|
if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
|
||||||
|
executorService.shutdownNow();
|
||||||
|
}
|
||||||
} catch (InterruptedException ie) {
|
} catch (InterruptedException ie) {
|
||||||
LOGGER.error("ERROR: Waiting on executor service shutdown!");
|
LOGGER.error("ERROR: Waiting on executor service shutdown!");
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,7 @@ Program output:
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
This is Ford.
|
This is Ford.
|
||||||
This Ferrari.
|
This is Ferrari.
|
||||||
```
|
```
|
||||||
|
|
||||||
## Class Diagram
|
## Class Diagram
|
||||||
|
Reference in New Issue
Block a user