From 4c22055e4772d121a622c8f7b8664517b016aaa8 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Sat, 25 Jul 2015 15:58:12 +0530 Subject: [PATCH] Implemented half sync half async pattern --- half-sync-half-async/pom.xml | 32 ++++++++++++ .../AsynchronousService.java | 51 +++++++++++++++++++ .../halfsynchalfasync/SynchronousLayer.java | 32 ++++++++++++ .../AsynchronousServiceTest.java | 45 ++++++++++++++++ 4 files changed, 160 insertions(+) create mode 100644 half-sync-half-async/pom.xml create mode 100644 half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsynchronousService.java create mode 100644 half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/SynchronousLayer.java create mode 100644 half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AsynchronousServiceTest.java diff --git a/half-sync-half-async/pom.xml b/half-sync-half-async/pom.xml new file mode 100644 index 000000000..a70b79ec8 --- /dev/null +++ b/half-sync-half-async/pom.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.5.0 + + half-sync-half-async + + + junit + junit + test + + + org.mockito + mockito-all + test + + + + + + org.mockito + mockito-all + 1.9.5 + + + + 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 new file mode 100644 index 000000000..81186b433 --- /dev/null +++ b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsynchronousService.java @@ -0,0 +1,51 @@ +package com.iluwatar.halfsynchalfasync; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * This is the asynchronous layer which does not block when a new request arrives. It just passes + * the request to the synchronous layer which consists of a queue i.e. a {@link BlockingQueue} and + * a pool of threads i.e. {@link ThreadPoolExecutor}. Out of this pool of threads one of the thread + * picks up the task and executes it in background and the result is posted back to the caller via + * {@link Future}. + */ +public abstract class AsynchronousService { + + /* + * This is the synchronous layer to which request to do work is submitted. + */ + private SynchronousLayer syncLayer = new SynchronousLayer(); + + /** + * Computes arithmetic sum for n + * + * @return future representing arithmetic sum of n + */ + public Future execute(final I input) { + /* + * This is the key part of this pattern where the caller thread does not block until + * the result of work is computed but is delegated to the synchronous layer which + * computes the task in background. This is useful if caller thread is an UI thread, + * which MUST remain responsive to user inputs. + */ + return syncLayer.submit(new Callable() { + + @Override + public O call() throws Exception { + return doInBackground(input); + } + + }); + } + + /** + * This method is called in context of background thread where the implementation should compute + * and return the result for input. + * + * @return computed result + */ + protected abstract O doInBackground(I input); +} diff --git a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/SynchronousLayer.java b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/SynchronousLayer.java new file mode 100644 index 000000000..9853ce8e2 --- /dev/null +++ b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/SynchronousLayer.java @@ -0,0 +1,32 @@ +package com.iluwatar.halfsynchalfasync; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * This represents the Queuing and Synchronous layer of Half-Sync/Half-Async pattern. + * The incoming requests are queued and then picked up by the background threads for execution. + */ +public class SynchronousLayer { + + /* + * This is the queuing layer where incoming work is queued + */ + private LinkedBlockingQueue tasks = new LinkedBlockingQueue(); + /* + * This is the synchronous layer where background threads execute the work + */ + private ExecutorService service = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, tasks); + + /** + * Submit new work for backgrounds threads to compute + * @return the result after executing the work + */ + public Future submit(Callable work) { + return service.submit(work); + } +} diff --git a/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AsynchronousServiceTest.java b/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AsynchronousServiceTest.java new file mode 100644 index 000000000..a938628df --- /dev/null +++ b/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AsynchronousServiceTest.java @@ -0,0 +1,45 @@ +package com.iluwatar.halfsynchalfasync; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import org.junit.Test; +import static org.junit.Assert.*; + +public class AsynchronousServiceTest { + + @Test + public void test() throws InterruptedException, ExecutionException { + /* + * Addition service is asynchronous layer which does not block on single request, + * and is always available for listening new requests. + */ + ArithmeticSumService service = new ArithmeticSumService(); + Future output1 = service.execute(100L); + Future output2 = service.execute(50L); + Future output3 = service.execute(200L); + Future output4 = service.execute(5L); + + assertEquals(ap(100), output1.get().longValue()); + assertEquals(ap(50), output2.get().longValue()); + assertEquals(ap(200), output3.get().longValue()); + assertEquals(ap(5), output4.get().longValue()); + } + + /* + * This is an asynchronous service which computes arithmetic sum + */ + class ArithmeticSumService extends AsynchronousService { + + @Override + protected Long doInBackground(Long n) { + return (n) * (n + 1) / 2; + } + } + + private long ap(int i) { + long out = (i) * (i + 1) / 2; + System.out.println(out); + return out; + } +}