diff --git a/half-sync-half-async/etc/half-sync-half-async.png b/half-sync-half-async/etc/half-sync-half-async.png new file mode 100644 index 000000000..84658dfde Binary files /dev/null and b/half-sync-half-async/etc/half-sync-half-async.png differ diff --git a/half-sync-half-async/etc/half-sync-half-async.ucls b/half-sync-half-async/etc/half-sync-half-async.ucls new file mode 100644 index 000000000..5b9941872 --- /dev/null +++ b/half-sync-half-async/etc/half-sync-half-async.ucls @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/App.java b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/App.java new file mode 100644 index 000000000..77b1988ba --- /dev/null +++ b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/App.java @@ -0,0 +1,118 @@ +package com.iluwatar.halfsynchalfasync; + +import java.util.concurrent.LinkedBlockingQueue; + +/** + * This application demonstrates Half-Sync/Half-Async pattern. Key parts of the pattern are + * {@link AsyncTask} and {@link AsynchronousService}. + * + *

+ * PROBLEM + *
+ * A concurrent system have a mixture of short duration, mid duration and long duration tasks. + * Mid or long duration tasks should be performed asynchronously to meet quality of service + * requirements. + * + *

INTENT + *
+ * The intent of this pattern is to separate the the synchronous and asynchronous processing + * in the concurrent application by introducing two intercommunicating layers - one for sync + * and one for async. This simplifies the programming without unduly affecting the performance. + * + *

+ * APPLICABILITY + *
+ *

+ * + *

+ * IMPLEMENTATION + *
+ * The main method creates an asynchronous service which does not block the main thread while + * the task is being performed. The main thread continues its work which is similar to Async Method + * Invocation pattern. The difference between them is that there is a queuing layer between Asynchronous + * layer and synchronous layer, which allows for different communication patterns between both layers. + * Such as Priority Queue can be used as queuing layer to prioritize the way tasks are executed. + * Our implementation is just one simple way of implementing this pattern, there are many variants possible + * as described in its applications. + */ +public class App { + + public static void main(String[] args) { + AsynchronousService service = new AsynchronousService(new LinkedBlockingQueue<>()); + /* + * A new task to calculate sum is received but as this is main thread, it should not block. + * So it passes it to the asynchronous task layer to compute and proceeds with handling other + * incoming requests. This is particularly useful when main thread is waiting on Socket to receive + * new incoming requests and does not wait for particular request to be completed before responding + * to new request. + */ + service.execute(new ArithmeticSumTask(1000)); + + /* New task received, lets pass that to async layer for computation. So both requests will be + * executed in parallel. + */ + service.execute(new ArithmeticSumTask(500)); + service.execute(new ArithmeticSumTask(2000)); + service.execute(new ArithmeticSumTask(1)); + } + + static class ArithmeticSumTask implements AsyncTask { + private long n; + + public ArithmeticSumTask(long n) { + this.n = n; + } + + /* + * This is the long running task that is performed in background. In our example + * the long running task is calculating arithmetic sum with artificial delay. + */ + @Override + public Long call() throws Exception { + return ap(n); + } + + /* + * This will be called in context of the main thread where some validations can be + * done regarding the inputs. Such as it must be greater than 0. It's a small + * computation which can be performed in main thread. If we did validated the input + * in background thread then we pay the cost of context switching + * which is much more than validating it in main thread. + */ + @Override + public void onPreCall() { + if (n < 0) { + throw new IllegalArgumentException("n is less than 0"); + } + } + + @Override + public void onPostCall(Long result) { + // Handle the result of computation + System.out.println(result); + } + + @Override + public void onError(Throwable throwable) { + throw new IllegalStateException("Should not occur"); + } + } + + private static long ap(long i) { + try { + Thread.sleep(i); + } catch (InterruptedException e) { + } + return (i) * (i + 1) / 2; + } +} diff --git a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsyncTask.java b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsyncTask.java index d773ff93f..8ed7376b6 100644 --- a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsyncTask.java +++ b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsyncTask.java @@ -13,20 +13,23 @@ import java.util.concurrent.Callable; public interface AsyncTask extends Callable { /** * Is called in context of caller thread before call to {@link #call()}. Large - * tasks should not be performed in this method. Validations can be performed here - * so that the performance penalty of context switching is not incurred in case of - * invalid requests. + * tasks should not be performed in this method as it will block the caller thread. + * Small tasks such as validations can be performed here so that the performance penalty + * of context switching is not incurred in case of invalid requests. */ - void preExecute(); + void onPreCall(); /** - * A callback called after the result is successfully computed by {@link #call()}. + * A callback called after the result is successfully computed by {@link #call()}. In our + * implementation this method is called in context of background thread but in some variants, + * such as Android where only UI thread can change the state of UI widgets, this method is called + * in context of UI thread. */ - void onResult(O result); + void onPostCall(O result); /** * A callback called if computing the task resulted in some exception. This method - * is called when either of {@link #call()} or {@link #preExecute()} throw any exception. + * is called when either of {@link #call()} or {@link #onPreCall()} throw any exception. * * @param throwable error cause */ diff --git a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsynchronousService.java b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsynchronousService.java index 8b858747e..6c36354d0 100644 --- a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsynchronousService.java +++ b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsynchronousService.java @@ -38,18 +38,16 @@ public class AsynchronousService { * A non-blocking method which performs the task provided in background and returns immediately. *

* On successful completion of task the result is posted back using callback method - * {@link AsyncTask#onResult(Object)}, if task execution is unable to complete normally + * {@link AsyncTask#onPostCall(Object)}, if task execution is unable to complete normally * due to some exception then the reason for error is posted back using callback method * {@link AsyncTask#onError(Throwable)}. *

* NOTE: The results are posted back in the context of background thread in this implementation. - * There is other variant possible where the result is posted back in the queue of caller thread - * and then the result is processed in context of caller thread. */ public void execute(final AsyncTask task) { try { // some small tasks such as validation can be performed here. - task.preExecute(); + task.onPreCall(); } catch (Exception e) { task.onError(e); } @@ -65,7 +63,7 @@ public class AsynchronousService { * where the UI elements can only be updated using UI thread. So result must be * posted back in UI thread. */ - task.onResult(get()); + task.onPostCall(get()); } catch (InterruptedException e) { // should not occur } catch (ExecutionException e) {