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 688d8482f..0b8ee3649 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 @@ -4,23 +4,24 @@ 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 + * 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. + * 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. + * 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. + * 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 @@ -32,66 +33,68 @@ import java.util.concurrent.Callable; */ public class App { - public static void main(String[] args) throws Exception { - // construct a new executor that will run async tasks - AsyncExecutor executor = new ThreadAsyncExecutor(); + 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")); + // 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"); + // 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(); + // 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); - } + // 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); - log("Task completed with: " + value); - return value; - }; - } + /** + * 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); + log("Task completed with: " + value); + return value; + }; + } - /** - * 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()) { - log(name + " failed: " + ex.map(Exception::getMessage).orElse("")); - } else { - log(name + ": " + value); - } - }; - } + /** + * 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()) { + log(name + " failed: " + ex.map(Exception::getMessage).orElse("")); + } else { + log(name + ": " + value); + } + }; + } - private static void log(String msg) { - System.out.println(String.format("[%1$-10s] - %2$s", Thread.currentThread().getName(), msg)); - } + private static void log(String msg) { + System.out.println(String.format("[%1$-10s] - %2$s", Thread.currentThread().getName(), msg)); + } } 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 46556a48e..8e4d77443 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 @@ -11,12 +11,11 @@ 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); - + /** + * 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 5c5098487..bd3c98339 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 @@ -10,33 +10,32 @@ 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. + * + * @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; + /** + * 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 405bec251..6d77df8ec 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 @@ -10,26 +10,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(); + /** + * 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; + /** + * 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; + /** + * 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 a3ed51af3..300934562 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 @@ -12,116 +12,117 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class ThreadAsyncExecutor implements AsyncExecutor { - /** Index for thread naming */ - private final AtomicInteger idx = new AtomicInteger(0); + /** Index for thread naming */ + private final AtomicInteger idx = new AtomicInteger(0); - @Override - public AsyncResult startProcess(Callable task) { - return startProcess(task, null); - } + @Override + public AsyncResult startProcess(Callable task) { + return startProcess(task, null); + } - @Override - public AsyncResult startProcess(Callable task, AsyncCallback callback) { - CompletableResult 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 AsyncResult startProcess(Callable task, AsyncCallback callback) { + CompletableResult 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 endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException { - if (asyncResult.isCompleted()) { - return asyncResult.getValue(); - } else { - asyncResult.await(); - return asyncResult.getValue(); - } - } + @Override + public T endProcess(AsyncResult asyncResult) throws ExecutionException, + InterruptedException { + if (asyncResult.isCompleted()) { + return asyncResult.getValue(); + } else { + asyncResult.await(); + return asyncResult.getValue(); + } + } - /** - * 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 { + /** + * 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; - static final int FAILED = 2; - static final int COMPLETED = 3; + static final int RUNNING = 1; + static final int FAILED = 2; + static final int COMPLETED = 3; - final Object lock; - final Optional> callback; + final Object lock; + final Optional> callback; - volatile int state = RUNNING; - T value; - Exception exception; + volatile int state = RUNNING; + T value; + Exception exception; - CompletableResult(AsyncCallback callback) { - this.lock = new Object(); - this.callback = Optional.ofNullable(callback); - } + CompletableResult(AsyncCallback callback) { + this.lock = new Object(); + 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; - this.callback.ifPresent(ac -> ac.onComplete(value, Optional.empty())); - synchronized (lock) { - lock.notifyAll(); - } - } + /** + * 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; + this.callback.ifPresent(ac -> ac.onComplete(value, Optional.empty())); + synchronized (lock) { + lock.notifyAll(); + } + } - /** - * 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; - this.callback.ifPresent(ac -> ac.onComplete(null, Optional.of(exception))); - synchronized (lock) { - lock.notifyAll(); - } - } + /** + * 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; + this.callback.ifPresent(ac -> ac.onComplete(null, Optional.of(exception))); + synchronized (lock) { + lock.notifyAll(); + } + } - @Override - public boolean isCompleted() { - return (state > RUNNING); - } + @Override + public boolean isCompleted() { + return (state > RUNNING); + } - @Override - public T getValue() throws ExecutionException { - if (state == COMPLETED) { - return value; - } else if (state == FAILED) { - throw new ExecutionException(exception); - } else { - throw new IllegalStateException("Execution not completed yet"); - } - } + @Override + public T getValue() throws ExecutionException { + if (state == COMPLETED) { + return value; + } else if (state == FAILED) { + throw new ExecutionException(exception); + } else { + throw new IllegalStateException("Execution not completed yet"); + } + } - @Override - public void await() throws InterruptedException { - synchronized (lock) { - if (!isCompleted()) { - lock.wait(); - } - } - } - } + @Override + public void await() throws InterruptedException { + synchronized (lock) { + if (!isCompleted()) { + lock.wait(); + } + } + } + } } diff --git a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java index 5faec5356..e077701c0 100644 --- a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java +++ b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java @@ -9,10 +9,9 @@ import org.junit.Test; */ public class AppTest { - @Test - public void test() throws Exception { - String[] args = {}; - App.main(args); - } - + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + } }