|
| 1 | +package com.iluwatar.halfsynchalfasync; |
| 2 | + |
| 3 | +import java.util.concurrent.LinkedBlockingQueue; |
| 4 | + |
| 5 | +/** |
| 6 | + * This application demonstrates Half-Sync/Half-Async pattern. Key parts of the pattern are |
| 7 | + * {@link AsyncTask} and {@link AsynchronousService}. |
| 8 | + * |
| 9 | + * <p> |
| 10 | + * <i>PROBLEM</i> |
| 11 | + * <br/> |
| 12 | + * A concurrent system have a mixture of short duration, mid duration and long duration tasks. |
| 13 | + * Mid or long duration tasks should be performed asynchronously to meet quality of service |
| 14 | + * requirements. |
| 15 | + * |
| 16 | + * <p><i>INTENT</i> |
| 17 | + * <br/> |
| 18 | + * The intent of this pattern is to separate the the synchronous and asynchronous processing |
| 19 | + * in the concurrent application by introducing two intercommunicating layers - one for sync |
| 20 | + * and one for async. This simplifies the programming without unduly affecting the performance. |
| 21 | + * |
| 22 | + * <p> |
| 23 | + * <i>APPLICABILITY</i> |
| 24 | + * <br/> |
| 25 | + * <ul> |
| 26 | + * <li>UNIX network subsystems - In operating systems network operations are carried out |
| 27 | + * asynchronously with help of hardware level interrupts.</li> |
| 28 | + * <li>CORBA - At the asynchronous layer one thread is associated with each socket that is |
| 29 | + * connected to the client. Thread blocks waiting for CORBA requests from the client. On receiving |
| 30 | + * request it is inserted in the queuing layer which is then picked up by synchronous layer which |
| 31 | + * processes the request and sends response back to the client.</li> |
| 32 | + * <li>Android AsyncTask framework - Framework provides a way to execute long running blocking calls, |
| 33 | + * such as downloading a file, in background threads so that the UI thread remains free to respond |
| 34 | + * to user inputs.</i> |
| 35 | + * </ul> |
| 36 | + * |
| 37 | + * <p> |
| 38 | + * <i>IMPLEMENTATION</i> |
| 39 | + * <br/> |
| 40 | + * The main method creates an asynchronous service which does not block the main thread while |
| 41 | + * the task is being performed. The main thread continues its work which is similar to Async Method |
| 42 | + * Invocation pattern. The difference between them is that there is a queuing layer between Asynchronous |
| 43 | + * layer and synchronous layer, which allows for different communication patterns between both layers. |
| 44 | + * Such as Priority Queue can be used as queuing layer to prioritize the way tasks are executed. |
| 45 | + * Our implementation is just one simple way of implementing this pattern, there are many variants possible |
| 46 | + * as described in its applications. |
| 47 | + */ |
| 48 | +public class App { |
| 49 | + |
| 50 | + public static void main(String[] args) { |
| 51 | + AsynchronousService service = new AsynchronousService(new LinkedBlockingQueue<>()); |
| 52 | + /* |
| 53 | + * A new task to calculate sum is received but as this is main thread, it should not block. |
| 54 | + * So it passes it to the asynchronous task layer to compute and proceeds with handling other |
| 55 | + * incoming requests. This is particularly useful when main thread is waiting on Socket to receive |
| 56 | + * new incoming requests and does not wait for particular request to be completed before responding |
| 57 | + * to new request. |
| 58 | + */ |
| 59 | + service.execute(new ArithmeticSumTask(1000)); |
| 60 | + |
| 61 | + /* New task received, lets pass that to async layer for computation. So both requests will be |
| 62 | + * executed in parallel. |
| 63 | + */ |
| 64 | + service.execute(new ArithmeticSumTask(500)); |
| 65 | + service.execute(new ArithmeticSumTask(2000)); |
| 66 | + service.execute(new ArithmeticSumTask(1)); |
| 67 | + } |
| 68 | + |
| 69 | + static class ArithmeticSumTask implements AsyncTask<Long> { |
| 70 | + private long n; |
| 71 | + |
| 72 | + public ArithmeticSumTask(long n) { |
| 73 | + this.n = n; |
| 74 | + } |
| 75 | + |
| 76 | + /* |
| 77 | + * This is the long running task that is performed in background. In our example |
| 78 | + * the long running task is calculating arithmetic sum with artificial delay. |
| 79 | + */ |
| 80 | + @Override |
| 81 | + public Long call() throws Exception { |
| 82 | + return ap(n); |
| 83 | + } |
| 84 | + |
| 85 | + /* |
| 86 | + * This will be called in context of the main thread where some validations can be |
| 87 | + * done regarding the inputs. Such as it must be greater than 0. It's a small |
| 88 | + * computation which can be performed in main thread. If we did validated the input |
| 89 | + * in background thread then we pay the cost of context switching |
| 90 | + * which is much more than validating it in main thread. |
| 91 | + */ |
| 92 | + @Override |
| 93 | + public void onPreCall() { |
| 94 | + if (n < 0) { |
| 95 | + throw new IllegalArgumentException("n is less than 0"); |
| 96 | + } |
| 97 | + } |
| 98 | + |
| 99 | + @Override |
| 100 | + public void onPostCall(Long result) { |
| 101 | + // Handle the result of computation |
| 102 | + System.out.println(result); |
| 103 | + } |
| 104 | + |
| 105 | + @Override |
| 106 | + public void onError(Throwable throwable) { |
| 107 | + throw new IllegalStateException("Should not occur"); |
| 108 | + } |
| 109 | + } |
| 110 | + |
| 111 | + private static long ap(long i) { |
| 112 | + try { |
| 113 | + Thread.sleep(i); |
| 114 | + } catch (InterruptedException e) { |
| 115 | + } |
| 116 | + return (i) * (i + 1) / 2; |
| 117 | + } |
| 118 | +} |
0 commit comments