diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java
index f59f831b2..de4dfe926 100644
--- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java
+++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java
@@ -2,30 +2,76 @@ package com.iluwatar.async.method.invocation;
import java.util.concurrent.Callable;
+/**
+ *
+ * This application demonstrates the async method invocation pattern. 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.
+ *
+ *
+ * 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 job it collects
+ * the results of the async tasks. Two of the tasks are handled with callbacks, meaning the callbacks are
+ * executed immediately when the tasks complete.
+ *
+ *
+ * Noteworthy difference of thread usage between the async results and callbacks is that the async results
+ * are collected in the main thread but the callbacks are executed within the worker threads. This should be
+ * noted when working with thread pools.
+ *
+ *
+ * Java provides its own implementations of async method invocation pattern. FutureTask, CompletableFuture
+ * and ExecutorService are the real world implementations of this pattern. But due to the nature of parallel
+ * programming, the implementations are not trivial. This example does not take all possible scenarios into
+ * account but rather provides a simple version that helps to understand the pattern.
+ *
+ *
+ * @see AsyncResult
+ * @see AsyncCallback
+ * @see AsyncExecutor
+ *
+ * @see java.util.concurrent.FutureTask
+ * @see java.util.concurrent.CompletableFuture
+ * @see java.util.concurrent.ExecutorService
+ */
public class App {
public static void main(String[] args) throws Exception {
+ // construct a new executor that will run async tasks
AsyncExecutor executor = new ThreadAsyncExecutor();
+
+ // start few async tasks with varying processing times, two last with callback handlers
AsyncResult asyncResult1 = executor.startProcess(lazyval(10, 500));
AsyncResult asyncResult2 = executor.startProcess(lazyval("test", 300));
AsyncResult asyncResult3 = executor.startProcess(lazyval(50L, 700));
AsyncResult asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Callback result 4"));
AsyncResult asyncResult5 = executor.startProcess(lazyval("callback", 600), callback("Callback result 5"));
+ // 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
log("Some hard work done");
+ // wait for completion of the tasks
Integer result1 = executor.endProcess(asyncResult1);
String result2 = executor.endProcess(asyncResult2);
Long result3 = executor.endProcess(asyncResult3);
asyncResult4.await();
asyncResult5.await();
+ // log the results of the tasks, callbacks log immediately when complete
log("Result 1: " + result1);
log("Result 2: " + result2);
log("Result 3: " + result3);
}
+ /**
+ * Creates a callable that lazily evaluates to given value with artificial delay.
+ *
+ * @param value value to evaluate
+ * @param delayMillis artificial delay in milliseconds
+ * @return new callable for lazy evaluation
+ */
private static Callable lazyval(T value, long delayMillis) {
return () -> {
Thread.sleep(delayMillis);
@@ -34,6 +80,12 @@ public class App {
};
}
+ /**
+ * Creates a simple callback that logs the complete status of the async result.
+ *
+ * @param name callback name
+ * @return new async callback
+ */
private static AsyncCallback callback(String name) {
return (value, ex) -> {
if (ex.isPresent()) {
diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java
index 0477f70e1..067b79d43 100644
--- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java
+++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java
@@ -4,6 +4,12 @@ import java.util.Optional;
public interface AsyncCallback {
+ /**
+ * Complete handler which is executed when async task is completed or fails execution.
+ *
+ * @param value the evaluated value from async task, undefined when execution fails
+ * @param ex empty value if execution succeeds, some exception if executions fails
+ */
void onComplete(T value, Optional ex);
}
diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java
index dd23a8bfb..4bf837a4f 100644
--- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java
+++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java
@@ -5,10 +5,33 @@ import java.util.concurrent.ExecutionException;
public interface AsyncExecutor {
+ /**
+ * Starts processing of an async task. Returns immediately with async result.
+ *
+ * @param task task to be executed asynchronously
+ * @return async result for the task
+ */
AsyncResult startProcess(Callable task);
+ /**
+ * Starts processing of an async task. Returns immediately with async result. Executes callback
+ * when the task is completed.
+ *
+ * @param task task to be executed asynchronously
+ * @param callback callback to be executed on task completion
+ * @return async result for the task
+ */
AsyncResult startProcess(Callable task, AsyncCallback callback);
+ /**
+ * Ends processing of an async task. Blocks the current thread if necessary and returns the
+ * evaluated value of the completed task.
+ *
+ * @param asyncResult async result of a task
+ * @return evaluated value of the completed task
+ * @throws ExecutionException if execution has failed, containing the root cause
+ * @throws InterruptedException if the execution is interrupted
+ */
T endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException;
}
diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java
index 5bf6145b8..689095e9d 100644
--- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java
+++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java
@@ -4,9 +4,26 @@ import java.util.concurrent.ExecutionException;
public interface AsyncResult {
+ /**
+ * Status of the async task execution.
+ *
+ * @return true
if execution is completed or failed
+ */
boolean isCompleted();
+ /**
+ * Gets the value of completed async task.
+ *
+ * @return evaluated value or throws ExecutionException if execution has failed
+ * @throws ExecutionException if execution has failed, containing the root cause
+ * @throws IllegalStateException if execution is not completed
+ */
T getValue() throws ExecutionException;
+ /**
+ * Blocks the current thread until the async task is completed.
+ *
+ * @throws InterruptedException if the execution is interrupted
+ */
void await() throws InterruptedException;
}
diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java
index b368e284d..18b27c3b6 100644
--- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java
+++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java
@@ -5,8 +5,12 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
+/**
+ * Implementation of async executor that creates a new thread for every task.
+ */
public class ThreadAsyncExecutor implements AsyncExecutor {
+ /** Index for thread naming */
private final AtomicInteger idx = new AtomicInteger(0);
@Override
@@ -37,6 +41,14 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
}
}
+ /**
+ * Simple implementation of async result that allows completing it successfully with a value
+ * or exceptionally with an exception. A really simplified version from its real life cousins
+ * FutureTask and CompletableFuture.
+ *
+ * @see java.util.concurrent.FutureTask
+ * @see java.util.concurrent.CompletableFuture
+ */
private static class CompletableResult implements AsyncResult {
static final int RUNNING = 1;
@@ -55,6 +67,12 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
this.callback = Optional.ofNullable(callback);
}
+ /**
+ * Sets the value from successful execution and executes callback if available. Notifies
+ * any thread waiting for completion.
+ *
+ * @param value value of the evaluated task
+ */
void setValue(T value) {
this.value = value;
this.state = COMPLETED;
@@ -64,6 +82,12 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
}
}
+ /**
+ * Sets the exception from failed execution and executes callback if available. Notifies
+ * any thread waiting for completion.
+ *
+ * @param exception exception of the failed task
+ */
void setException(Exception exception) {
this.exception = exception;
this.state = FAILED;