├── .gitignore ├── README.md ├── active-object ├── README.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── org │ │ └── zezutom │ │ └── concurrencypatterns │ │ └── activeobject │ │ ├── Counter.java │ │ ├── ThreadSafeCounter.java │ │ └── ThreadUnsafeCounter.java │ └── test │ └── java │ └── org │ └── zezutom │ └── concurrency │ └── patterns │ └── activeobject │ └── test │ ├── ThreadSafeCounterMultiThreadedTest.java │ ├── ThreadSafeCounterSingleThreadedTest.java │ ├── ThreadUnsafeCounterMultiThreadedTest.java │ └── ThreadUnsafeCounterSingleThreadedTest.java ├── common ├── pom.xml └── src │ ├── main │ └── java │ │ └── org │ │ └── zezutom │ │ └── concurrencypatterns │ │ └── test │ │ └── util │ │ ├── DataUtil.java │ │ ├── StopWatch.java │ │ ├── TestExecutor.java │ │ └── TestRunner.java │ └── test │ └── java │ └── org │ └── zezutom │ └── concurrencypatterns │ └── test │ └── util │ └── StopWatchTest.java ├── half-sync-half-async ├── README.md ├── data │ └── audrey.txt ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── zezutom │ │ │ └── concurrencypatterns │ │ │ └── halfsynchalfasync │ │ │ ├── AsciiArtGenerator.java │ │ │ ├── BlockingDispatcher.java │ │ │ ├── NonBlockingDispatcher.java │ │ │ ├── ResultSubscriber.java │ │ │ └── WorkQueue.java │ └── resources │ │ └── audrey_hepburn.jpg │ └── test │ └── java │ └── org │ └── zezutom │ └── concurrencypatterns │ └── halfsynchalfasync │ └── test │ ├── AsyncResultSubscriber.java │ ├── BlockingDispatcherTest.java │ └── NonBlockingDispatcherTest.java ├── monitor-object ├── README.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── org │ │ └── zezutom │ │ └── concurrencypatterns │ │ └── monitorobject │ │ ├── CleanToilet.java │ │ ├── FilthyToilet.java │ │ ├── Toilet.java │ │ └── ToiletFloodedException.java │ └── test │ └── java │ └── org │ └── zezutom │ └── concurrencypatterns │ └── monitorobject │ └── test │ ├── CleanToiletMultiThreadedTest.java │ ├── CleanToiletSingleThreadedTest.java │ ├── FilthyToiletMultiThreadedTest.java │ └── FilthyToiletSingleThreadedTest.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | target 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | | Pattern | Main Features | Drawbacks | 3 | | ------- | ------------- | --------- | 4 | | [Active Object](./active-object) | execution in a dedicated thread, allows for complex scheduling, good separation of concerns | performance / code overhead | 5 | | [Monitor Object](./monitor-object) | cooperative execution scheduling, less of performance overhead | tight coupling, unsuitable for advanced scheduling | 6 | | [Half-Sync / Half-Async](./half-sync-half-async) | responsive interface, separation of concerns | performance overhead, harder to debug | 7 | | Leader / Followers | | | 8 | | Thread-Specific Storage | | | 9 | 10 | -------------------------------------------------------------------------------- /active-object/README.md: -------------------------------------------------------------------------------- 1 | # Active Object 2 | 3 | The goal is to decouple method execution from its invocation. Why? Well, to either achieve 4 | a better throughput via asynchronous method invocation or work around system limitations or both, examples: 5 | - booking system: instantaneous request confirmation vs time of when the order is actually processed 6 | - Android programming - UI changes: a background service sending a message to the UI thread via a message handler 7 | 8 | To avoid race conditions, incoming client requests are queued and handled by a scheduler. 9 | The scheduler picks a queued object and makes it run its logic. It is object's responsibility 10 | to know what to do when it gets invoked, hence the Active Object. 11 | 12 | ## Key Components 13 | - __Proxy__: provides interface the clients can use to submit their requests 14 | - __Activation List__: a queue of pending client requests 15 | - __Scheduler__: decides which request to execute next 16 | - __Active Object__: implements the core business logic 17 | - __Callback__: contains execution result (i.e. a promise or a future) 18 | 19 | ## Pros and Cons 20 | On the bright side: 21 | - __Reduced code complexity__: Once pattern's mechanics are in place, the code can be treated as single-threaded. 22 | - __No need for additional synchronization__: Concurrent requests are serialized and handled by a single internal thread 23 | 24 | On the down side: 25 | - __Performance overhead__: Sophisticated scheduling, spinning and request handling can be expensive in terms of memory and can lead to non-trivial context switching. 26 | - __Programming overhead__: Active Object essentially requires you to create a small framework. It can definitely be kept self-contained enough, but it boils down to a simple the fact that you need to be aware of multiple components: 27 | 28 | Activation List - the queue of incoming requests 29 | 30 | Callback - yields the results 31 | 32 | Scheduler thread - watches for incoming requests 33 | 34 | Scheduler implementation - enqueues requests 35 | 36 | Proxy - client interface allowing to submit requests 37 | 38 | Future - an asynchronous response 39 | 40 | ## Example 41 | source code directories: 42 | - `src/main/java/org/zezutom/concurrencypatterns/activeobject` 43 | - `src/main/java/org/zezutom/concurrencypatterns/activeobject/test` 44 | 45 | A simple counter implementing a sub-set of the [AtomicLong](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicLong.html). 46 | The counter keeps its internal state which is then a subject to race conditions: 47 | ```java 48 | public class ThreadSafeCounter implements Counter { 49 | private long value; 50 | .. 51 | } 52 | ``` 53 | The challenge is to ensure the counter consistently yields the correct results, even when many 54 | threads access and modify counter's intrinsic value. 55 | 56 | `ThreadUnsafeCounter.java` represents a naive implementation which fails to handle concurrent access. 57 | The failure is proved by a multi-threaded test `ThreadUnsafeCounterMultiThreadedTest.java`: 58 | 59 | ```java 60 | public class ThreadUnsafeCounterMultiThreadedTest { 61 | .. 62 | // Note that a test failure is expected 63 | @Test(expected = AssertionError.class) 64 | public void incrementAndGet() { 65 | testExecutor.runTest(incrementAndGetCommand); 66 | assertEquals(getExpectedIncrementedValue(), counter.get()); 67 | } 68 | .. 69 | } 70 | ``` 71 | 72 | `ThreadSafeCounter.java` handles concurrency by using the Active Object design pattern: 73 | 74 | ```java 75 | public class ThreadSafeCounter implements Counter { 76 | 77 | // The internal state, subject to race conditions. 78 | private long value; 79 | 80 | // Activation List: incoming requests (tasks) are put into a queue 81 | private BlockingQueue> taskQueue = new LinkedBlockingQueue<>(); 82 | 83 | // Callback: provides access to the calculated results (incrementAndGet, etc.) 84 | private BlockingQueue resultQueue = new LinkedBlockingQueue<>(); 85 | 86 | // Scheduler: a dedicated thread created and started when the counter is instantiated 87 | public ThreadSafeCounter(long value) { 88 | .. 89 | new Thread(new Runnable() { 90 | @Override 91 | public void run() { 92 | while (true) { 93 | // Constantly watching for incoming requests 94 | .. 95 | } 96 | } 97 | }).start(); 98 | } 99 | .. 100 | 101 | // Proxy: allows the clients to submit new tasks 102 | private long enqueueTask(Callable task) {..} 103 | } 104 | ``` 105 | 106 | The implementation offloads the actual task scheduling to the [Executor](http://docs.oracle.com/javase/tutorial/essential/concurrency/exinter.html) framework. 107 | The execution results are handled asynchronously via futures. For simplicity, I chose to block 108 | the clients until the results become available. Still in the `ThreadSafeCounter.java`: 109 | 110 | ```java 111 | // This is the actual task scheduler. It only allows for a single task at a time. 112 | ExecutorService executorService = Executors.newSingleThreadExecutor(); 113 | .. 114 | // At some point in the future the counter's new value will be available 115 | Future future = executorService.submit(taskQueue.take()); 116 | .. 117 | // Meanwhile, the client is blocked until the result is ready 118 | while (true) { 119 | Long result = resultQueue.poll(500, TimeUnit.MILLISECONDS); 120 | if (result != null) break; 121 | } 122 | .. 123 | ``` 124 | 125 | ## Resources 126 | - [Wikipedia](http://en.wikipedia.org/wiki/Active_object) 127 | - [Prefer Using Active Objects instead of Naked Threads](http://www.drdobbs.com/parallel/prefer-using-active-objects-instead-of-n/225700095) 128 | - [The Pragmatic Bookshelf: Java Active Objects](http://pragprog.com/magazines/2013-05/java-active-objects) 129 | - [Android Concurrency: The Active Object Pattern](http://www.dre.vanderbilt.edu/~schmidt/cs282/PDFs/6-Concurrency-and-Synchronization-part9.pdf) 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /active-object/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | concurrency-patterns 7 | org.zezutom 8 | ${my.version} 9 | 10 | 4.0.0 11 | 12 | active-object 13 | 14 | 15 | 16 | org.zezutom 17 | common 18 | ${my.version} 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /active-object/src/main/java/org/zezutom/concurrencypatterns/activeobject/Counter.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.activeobject; 2 | 3 | /** 4 | * @author Tomas Zezula 5 | * 6 | * This interface shadows a subset of functionality of java.util.concurrent.atomic.AtomicLong 7 | */ 8 | public interface Counter { 9 | 10 | /** 11 | * Gets the current value 12 | * @return the current value 13 | */ 14 | long get(); 15 | 16 | /** 17 | * Atomically increments the value by one 18 | * @return the incremented value 19 | */ 20 | long incrementAndGet(); 21 | 22 | /** 23 | * Atomically increments the value by one 24 | * @return the current (non-incremented) value 25 | */ 26 | long getAndIncrement(); 27 | 28 | /** 29 | * Atomically decrements the value by one 30 | * @return the decremented value 31 | */ 32 | long decrementAndGet(); 33 | 34 | /** 35 | * Atomically decrements the value by one 36 | * @return the current (non-decremented) value 37 | */ 38 | long getAndDecrement(); 39 | } 40 | -------------------------------------------------------------------------------- /active-object/src/main/java/org/zezutom/concurrencypatterns/activeobject/ThreadSafeCounter.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.activeobject; 2 | 3 | import java.util.concurrent.*; 4 | 5 | /** 6 | * @author Tomas Zezula 7 | * 8 | * Implements the org.zezutom.concurrencypatterns.org.zezutom.concurrencypatterns.activeobject.Counter in a way that is suitable 9 | * for multi-threaded scenarios. The implementation makes use of the Active Object 10 | * design pattern: 11 | * 12 | * - This implementation is referred to as Active Object (active object) 13 | * - All of the work is done on a private thread 14 | * - Method calls are enqueued to the active object and the caller is returned to instantly 15 | * (method calls on the active object are always non-blocking and asynchronous) 16 | * - The private thread is essentially a message taskQueue 17 | * - Messages are being dequeued and executed one at a time 18 | * - Messages are atomic one to each other, because they are processed sequentially 19 | * - Private data are accessed from the private thread 20 | * - Since there is not a 'shared' state, there is no need for additional synchronization either 21 | */ 22 | public class ThreadSafeCounter implements Counter { 23 | 24 | // The internal state, subject to race conditions. 25 | private long value; 26 | 27 | // Activation List: incoming requests (tasks) are put into a queue 28 | private BlockingQueue> taskQueue = new LinkedBlockingQueue<>(); 29 | 30 | // Callback: provides access to the calculated results (incrementAndGet, etc.) 31 | private BlockingQueue resultQueue = new LinkedBlockingQueue<>(); 32 | 33 | // Scheduler: a dedicated thread created and started when the counter gets instantiated 34 | public ThreadSafeCounter(long value) { 35 | this.value = value; 36 | 37 | new Thread(new Runnable() { 38 | @Override 39 | public void run() { 40 | // This is the actual task scheduler. It only allows for a single task at a time. 41 | ExecutorService executorService = Executors.newSingleThreadExecutor(); 42 | try { 43 | // busy waiting 44 | while (true) { 45 | try { 46 | // At some point in the future the counter's new value will be available 47 | Future future = executorService.submit(taskQueue.take()); 48 | while (!future.isDone()) 49 | ; // wait until the results are ready 50 | resultQueue.put(future.get()); 51 | } catch (InterruptedException | ExecutionException e) { 52 | throw new RuntimeException("Task execution was failed!"); 53 | } 54 | } 55 | } 56 | finally { 57 | executorService.shutdown(); 58 | } 59 | } 60 | }).start(); 61 | } 62 | 63 | @Override 64 | public long get() { 65 | return enqueueTask(new Callable() { 66 | @Override 67 | public Long call() throws Exception { 68 | return value; 69 | } 70 | }); 71 | } 72 | 73 | @Override 74 | public long incrementAndGet() { 75 | return enqueueTask(new Callable() { 76 | @Override 77 | public Long call() throws Exception { 78 | return ++value; 79 | } 80 | }); 81 | } 82 | 83 | @Override 84 | public long getAndIncrement() { 85 | return enqueueTask(new Callable() { 86 | @Override 87 | public Long call() throws Exception { 88 | return value++; 89 | } 90 | }); 91 | } 92 | 93 | @Override 94 | public long decrementAndGet() { 95 | return enqueueTask(new Callable() { 96 | @Override 97 | public Long call() throws Exception { 98 | return --value; 99 | } 100 | }); 101 | } 102 | 103 | @Override 104 | public long getAndDecrement() { 105 | return enqueueTask(new Callable() { 106 | @Override 107 | public Long call() throws Exception { 108 | return value--; 109 | } 110 | }); 111 | } 112 | 113 | // Proxy: allows the clients to submit new tasks 114 | private long enqueueTask(Callable task) { 115 | Long result; 116 | try { 117 | // Put the task into the queue 118 | taskQueue.put(task); 119 | 120 | // Meanwhile, the client is blocked until the result is ready 121 | while (true) { 122 | result = resultQueue.poll(500, TimeUnit.MILLISECONDS); 123 | if (result != null) break; 124 | } 125 | return result; 126 | } catch (InterruptedException e) { 127 | throw new RuntimeException("Task scheduling was interrupted!"); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /active-object/src/main/java/org/zezutom/concurrencypatterns/activeobject/ThreadUnsafeCounter.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.activeobject; 2 | 3 | /** 4 | * @author Tomas Zezula 5 | * 6 | * Implements the org.zezutom.concurrencypatterns.org.zezutom.concurrencypatterns.activeobject.Counter in a way that is NOT suitable 7 | * for multi-threaded scenarios. 8 | */ 9 | public class ThreadUnsafeCounter implements Counter { 10 | 11 | private long value; 12 | 13 | public ThreadUnsafeCounter(long value) { 14 | this.value = value; 15 | } 16 | 17 | @Override 18 | public long get() { 19 | return value; 20 | } 21 | 22 | @Override 23 | public long incrementAndGet() { 24 | return ++value; 25 | } 26 | 27 | @Override 28 | public long getAndIncrement() { 29 | return value++; 30 | } 31 | 32 | @Override 33 | public long decrementAndGet() { 34 | return --value; 35 | } 36 | 37 | @Override 38 | public long getAndDecrement() { 39 | return value--; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /active-object/src/test/java/org/zezutom/concurrency/patterns/activeobject/test/ThreadSafeCounterMultiThreadedTest.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrency.patterns.activeobject.test; 2 | 3 | import org.junit.Before; 4 | import org.junit.BeforeClass; 5 | import org.junit.Test; 6 | import org.zezutom.concurrencypatterns.activeobject.Counter; 7 | import org.zezutom.concurrencypatterns.activeobject.ThreadSafeCounter; 8 | import org.zezutom.concurrencypatterns.test.util.TestExecutor; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | /** 13 | * @author Tomas Zezula 14 | * 15 | * Proves that the implementation of org.zezutom.concurrencypatterns.org.zezutom.concurrencypatterns.activeobject.ThreadSafeCounter 16 | * is thread-safe, as the counter - under race conditions - consistently returns expected values. 17 | */ 18 | public class ThreadSafeCounterMultiThreadedTest { 19 | 20 | // The value the counter is initialized with 21 | public static final long INITIAL_VALUE = 10L; 22 | 23 | // Concurrent counter threads: per-method commands 24 | private static Runnable getCommand; 25 | private static Runnable incrementAndGetCommand; 26 | private static Runnable getAndIncrementCommand; 27 | private static Runnable decrementAndGetCommand; 28 | private static Runnable getAndDecrementCommand; 29 | 30 | // Multi-threaded org.zezutom.concurrencypatterns.monitorobject.test executor 31 | private static TestExecutor testExecutor; 32 | 33 | // An instance of the tested class. Being 'volatile' indicates it's going to be used by multiple threads 34 | private static volatile Counter counter; 35 | 36 | // The value of the counter prior to any testing 37 | private long startValue; 38 | 39 | @BeforeClass 40 | public static void init() { 41 | // Instantiates the counter with the initial value 42 | counter = new ThreadSafeCounter(INITIAL_VALUE); 43 | 44 | // Initializes multi-threaded org.zezutom.concurrencypatterns.monitorobject.test executor 45 | testExecutor = TestExecutor.get(); 46 | 47 | // Initializes individual commands 48 | getCommand = new Runnable() {@Override public void run() { counter.get(); } }; 49 | incrementAndGetCommand = new Runnable() {@Override public void run() { counter.incrementAndGet(); } }; 50 | getAndIncrementCommand = new Runnable() {@Override public void run() { counter.getAndIncrement(); } }; 51 | decrementAndGetCommand = new Runnable() {@Override public void run() { counter.decrementAndGet(); } }; 52 | getAndDecrementCommand = new Runnable() {@Override public void run() { counter.getAndDecrement(); } }; 53 | } 54 | 55 | @Before 56 | public void setUp() { 57 | startValue = counter.get(); 58 | } 59 | 60 | @Test 61 | public void get() { 62 | testExecutor.runTest(getCommand); 63 | assertEquals(startValue, counter.get()); 64 | } 65 | 66 | @Test 67 | public void incrementAndGet() { 68 | testExecutor.runTest(incrementAndGetCommand); 69 | assertEquals(getExpectedIncrementedValue(), counter.get()); 70 | } 71 | 72 | @Test 73 | public void getAndIncrement() { 74 | testExecutor.runTest(getAndIncrementCommand); 75 | assertEquals(getExpectedIncrementedValue(), counter.get()); 76 | } 77 | 78 | @Test 79 | public void decrementAndGet() { 80 | testExecutor.runTest(decrementAndGetCommand); 81 | assertEquals(getExpectedDecrementedValue(), counter.get()); 82 | } 83 | 84 | @Test 85 | public void getAndDecrement() { 86 | testExecutor.runTest(getAndDecrementCommand); 87 | assertEquals(getExpectedDecrementedValue(), counter.get()); 88 | } 89 | 90 | @Test 91 | public void runAll() { 92 | testExecutor.runTest(getCommand, 93 | incrementAndGetCommand, 94 | getAndIncrementCommand, 95 | decrementAndGetCommand, 96 | getAndDecrementCommand); 97 | assertEquals(startValue, counter.get()); 98 | } 99 | 100 | private long getExpectedIncrementedValue() { 101 | return startValue + TestExecutor.MAX_ITERATIONS * TestExecutor.DEFAULT_CONCURRENT_THREADS; 102 | } 103 | 104 | private long getExpectedDecrementedValue() { 105 | return startValue - TestExecutor.MAX_ITERATIONS * TestExecutor.DEFAULT_CONCURRENT_THREADS; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /active-object/src/test/java/org/zezutom/concurrency/patterns/activeobject/test/ThreadSafeCounterSingleThreadedTest.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrency.patterns.activeobject.test; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import org.zezutom.concurrencypatterns.activeobject.Counter; 6 | import org.zezutom.concurrencypatterns.activeobject.ThreadSafeCounter; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | 10 | /** 11 | * @author Tomas Zezula 12 | * 13 | * Proves the core functionality, all tests should pass. 14 | */ 15 | public class ThreadSafeCounterSingleThreadedTest { 16 | 17 | // The value the counter is initialized with 18 | public static final long INITIAL_VALUE = 10L; 19 | 20 | private Counter counter; 21 | 22 | @Before 23 | public void init() { 24 | counter = new ThreadSafeCounter(INITIAL_VALUE); 25 | } 26 | 27 | @Test 28 | public void get() { 29 | assertEquals(INITIAL_VALUE, counter.get()); 30 | } 31 | 32 | @Test 33 | public void incrementAndGet() { 34 | final long expected = INITIAL_VALUE + 1; 35 | assertEquals(expected, counter.incrementAndGet()); 36 | assertEquals(expected, counter.get()); 37 | }; 38 | 39 | @Test 40 | public void getAndIncrement() { 41 | assertEquals(INITIAL_VALUE, counter.getAndIncrement()); 42 | assertEquals(INITIAL_VALUE + 1, counter.get()); 43 | }; 44 | 45 | @Test 46 | public void decrementAndGet() { 47 | final long expected = INITIAL_VALUE - 1; 48 | assertEquals(expected, counter.decrementAndGet()); 49 | assertEquals(expected, counter.get()); 50 | }; 51 | 52 | @Test 53 | public void getAndDecrement() { 54 | assertEquals(INITIAL_VALUE, counter.getAndDecrement()); 55 | assertEquals(INITIAL_VALUE - 1, counter.get()); 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /active-object/src/test/java/org/zezutom/concurrency/patterns/activeobject/test/ThreadUnsafeCounterMultiThreadedTest.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrency.patterns.activeobject.test; 2 | 3 | import org.junit.Before; 4 | import org.junit.BeforeClass; 5 | import org.junit.Test; 6 | import org.zezutom.concurrencypatterns.activeobject.Counter; 7 | import org.zezutom.concurrencypatterns.activeobject.ThreadUnsafeCounter; 8 | import org.zezutom.concurrencypatterns.test.util.TestExecutor; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | /** 13 | * @author Tomas Zezula 14 | * 15 | * Proves that the implementation of org.zezutom.concurrencypatterns.org.zezutom.concurrencypatterns.activeobject.ThreadUnsafeCounter 16 | * is NOT thread-safe. The tests "pass" in a a sense that the actual results differ from 17 | * the expected values. 18 | */ 19 | public class ThreadUnsafeCounterMultiThreadedTest { 20 | 21 | // The value the counter is initialized with 22 | public static final long INITIAL_VALUE = 10L; 23 | 24 | // Concurrent counter threads: per-method commands 25 | private static Runnable getCommand; 26 | private static Runnable incrementAndGetCommand; 27 | private static Runnable getAndIncrementCommand; 28 | private static Runnable decrementAndGetCommand; 29 | private static Runnable getAndDecrementCommand; 30 | 31 | // Multi-threaded org.zezutom.concurrencypatterns.monitorobject.test executor 32 | private static TestExecutor testExecutor; 33 | 34 | // An instance of the tested class. Being 'volatile' indicates it's going to be used by multiple threads 35 | private static volatile Counter counter; 36 | 37 | // The value of the counter prior to any testing 38 | private long startValue; 39 | 40 | @BeforeClass 41 | public static void init() { 42 | // Instantiates the counter with the initial value 43 | counter = new ThreadUnsafeCounter(INITIAL_VALUE); 44 | 45 | // Initializes multi-threaded org.zezutom.concurrencypatterns.monitorobject.test executor 46 | testExecutor = TestExecutor.get(); 47 | 48 | // Initializes individual commands 49 | getCommand = new Runnable() {@Override public void run() { counter.get(); } }; 50 | incrementAndGetCommand = new Runnable() {@Override public void run() { counter.incrementAndGet(); } }; 51 | getAndIncrementCommand = new Runnable() {@Override public void run() { counter.getAndIncrement(); } }; 52 | decrementAndGetCommand = new Runnable() {@Override public void run() { counter.decrementAndGet(); } }; 53 | getAndDecrementCommand = new Runnable() {@Override public void run() { counter.getAndDecrement(); } }; 54 | } 55 | 56 | @Before 57 | public void setUp() { 58 | startValue = counter.get(); 59 | } 60 | 61 | @Test 62 | public void get() { 63 | testExecutor.runTest(getCommand); 64 | assertEquals(startValue, counter.get()); 65 | } 66 | 67 | @Test(expected = AssertionError.class) 68 | public void incrementAndGet() { 69 | testExecutor.runTest(incrementAndGetCommand); 70 | assertEquals(getExpectedIncrementedValue(), counter.get()); 71 | } 72 | 73 | @Test(expected = AssertionError.class) 74 | public void getAndIncrement() { 75 | testExecutor.runTest(getAndIncrementCommand); 76 | assertEquals(getExpectedIncrementedValue(), counter.get()); 77 | } 78 | 79 | @Test(expected = AssertionError.class) 80 | public void decrementAndGet() { 81 | testExecutor.runTest(decrementAndGetCommand); 82 | assertEquals(getExpectedDecrementedValue(), counter.get()); 83 | } 84 | 85 | @Test(expected = AssertionError.class) 86 | public void getAndDecrement() { 87 | testExecutor.runTest(getAndDecrementCommand); 88 | assertEquals(getExpectedDecrementedValue(), counter.get()); 89 | } 90 | 91 | @Test(expected = AssertionError.class) 92 | public void runAll() { 93 | testExecutor.runTest(getCommand, 94 | incrementAndGetCommand, 95 | getAndIncrementCommand, 96 | decrementAndGetCommand, 97 | getAndDecrementCommand); 98 | assertEquals(startValue, counter.get()); 99 | } 100 | 101 | private long getExpectedIncrementedValue() { 102 | return startValue + TestExecutor.MAX_ITERATIONS * TestExecutor.DEFAULT_CONCURRENT_THREADS; 103 | } 104 | 105 | private long getExpectedDecrementedValue() { 106 | return startValue - TestExecutor.MAX_ITERATIONS * TestExecutor.DEFAULT_CONCURRENT_THREADS; 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /active-object/src/test/java/org/zezutom/concurrency/patterns/activeobject/test/ThreadUnsafeCounterSingleThreadedTest.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrency.patterns.activeobject.test; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import org.zezutom.concurrencypatterns.activeobject.Counter; 6 | import org.zezutom.concurrencypatterns.activeobject.ThreadUnsafeCounter; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | 10 | /** 11 | * @author Tomas Zezula 12 | * 13 | * Proves the core functionality, all tests should pass. 14 | */ 15 | public class ThreadUnsafeCounterSingleThreadedTest { 16 | 17 | // The value the counter is initialized with 18 | public static final long INITIAL_VALUE = 10L; 19 | 20 | private Counter counter; 21 | 22 | @Before 23 | public void init() { 24 | counter = new ThreadUnsafeCounter(INITIAL_VALUE); 25 | } 26 | 27 | @Test 28 | public void get() { 29 | assertEquals(INITIAL_VALUE, counter.get()); 30 | } 31 | 32 | @Test 33 | public void incrementAndGet() { 34 | final long expected = INITIAL_VALUE + 1; 35 | assertEquals(expected, counter.incrementAndGet()); 36 | assertEquals(expected, counter.get()); 37 | }; 38 | 39 | @Test 40 | public void getAndIncrement() { 41 | assertEquals(INITIAL_VALUE, counter.getAndIncrement()); 42 | assertEquals(INITIAL_VALUE + 1, counter.get()); 43 | }; 44 | 45 | @Test 46 | public void decrementAndGet() { 47 | final long expected = INITIAL_VALUE - 1; 48 | assertEquals(expected, counter.decrementAndGet()); 49 | assertEquals(expected, counter.get()); 50 | }; 51 | 52 | @Test 53 | public void getAndDecrement() { 54 | assertEquals(INITIAL_VALUE, counter.getAndDecrement()); 55 | assertEquals(INITIAL_VALUE - 1, counter.get()); 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | concurrency-patterns 7 | org.zezutom 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | common 13 | 14 | 15 | -------------------------------------------------------------------------------- /common/src/main/java/org/zezutom/concurrencypatterns/test/util/DataUtil.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.test.util; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * @author: Tomas Zezula 7 | * Date: 28/08/2014 8 | */ 9 | public class DataUtil { 10 | 11 | private DataUtil() {} 12 | 13 | public static File getFile(String filename) { 14 | return new File(System.getProperty("user.dir") + "/data/" + filename); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /common/src/main/java/org/zezutom/concurrencypatterns/test/util/StopWatch.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.test.util; 2 | 3 | /** 4 | * @author: Tomas Zezula 5 | * Date: 26/08/2014 6 | */ 7 | public class StopWatch { 8 | 9 | private long startTime = 0; 10 | 11 | private long stopTime = 0; 12 | 13 | private boolean running = false; 14 | 15 | public void start() { 16 | startTime = System.currentTimeMillis(); 17 | running = true; 18 | } 19 | 20 | public void stop() { 21 | stopTime = System.currentTimeMillis(); 22 | running = false; 23 | } 24 | 25 | public long elapsedTime() { 26 | return (running) ? System.currentTimeMillis() - startTime : stopTime - startTime; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /common/src/main/java/org/zezutom/concurrencypatterns/test/util/TestExecutor.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.test.util; 2 | 3 | import java.util.concurrent.CountDownLatch; 4 | import java.util.concurrent.CyclicBarrier; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | import static org.junit.Assert.fail; 8 | 9 | /** 10 | * @author Tomas Zezula 11 | * Date: 27/07/2014 12 | */ 13 | public class TestExecutor { 14 | 15 | // The number of iteration the commands should be looped over 16 | public static final int MAX_ITERATIONS = 10000; 17 | 18 | // The number of concurrent threads per each individual command 19 | public static final int DEFAULT_CONCURRENT_THREADS = 5; 20 | 21 | private int iterations; 22 | 23 | private int concurrentThreads; 24 | 25 | // Ensures all threads are ready when starting a new test 26 | private CyclicBarrier startSync; 27 | 28 | // Ensures all threads are done doing their job before a test is terminated 29 | private CountDownLatch stopSync; 30 | 31 | // A number of concurrent tests 32 | TestRunner[] runners; 33 | 34 | private TestExecutor(int iterations, int concurrentThreads) { 35 | this.iterations = iterations; 36 | this.concurrentThreads = concurrentThreads; 37 | } 38 | 39 | public static TestExecutor get() { 40 | return new TestExecutor(MAX_ITERATIONS, DEFAULT_CONCURRENT_THREADS); 41 | } 42 | 43 | public static TestExecutor getSingle() { 44 | return get(1, 1); 45 | } 46 | 47 | public static TestExecutor get(int iterations, int concurrentThreads) { 48 | return new TestExecutor(iterations, concurrentThreads); 49 | } 50 | 51 | public void runTest(Runnable... commands) { 52 | 53 | if (commands == null || commands.length == 0) { 54 | throw new IllegalArgumentException("No commands to execute!"); 55 | } 56 | try { 57 | 58 | if (commands.length > 1) { 59 | // Initialize the runners with the predefined commands 60 | initTestSync(commands.length); 61 | for (int i = 0; i < runners.length; i++) { 62 | runners[i] = new TestRunner(this, commands[i]); 63 | } 64 | } 65 | else { 66 | // Initialize the runners with the provided command and start them 67 | initTestSync(concurrentThreads); 68 | for (int i = 0; i < runners.length; i++) { 69 | runners[i] = new TestRunner(this, commands[0]); 70 | } 71 | } 72 | 73 | // Start the runners 74 | for (TestRunner runner : runners) { 75 | new Thread(runner).start(); 76 | } 77 | 78 | // Wait until all of the runners will have finished their job 79 | stopSync.await(); 80 | 81 | // Ensure each and every runner did actually do its job 82 | for (TestRunner runner : runners) { 83 | assertEquals("The runner not used to its full potential.", iterations, runner.getIterations()); 84 | } 85 | 86 | } catch (InterruptedException | AssertionError e) { 87 | fail("Exception when running the tests."); 88 | } 89 | } 90 | 91 | public CyclicBarrier getStartSync() { 92 | return startSync; 93 | } 94 | 95 | public CountDownLatch getStopSync() { 96 | return stopSync; 97 | } 98 | 99 | public int getIterations() { 100 | return iterations; 101 | } 102 | 103 | private void initTestSync(final int threadCount) { 104 | // Tests synchronization 105 | startSync = new CyclicBarrier(threadCount); 106 | stopSync = new CountDownLatch(threadCount); 107 | runners = new TestRunner[threadCount]; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /common/src/main/java/org/zezutom/concurrencypatterns/test/util/TestRunner.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.test.util; 2 | 3 | import java.util.concurrent.BrokenBarrierException; 4 | 5 | import static org.junit.Assert.fail; 6 | 7 | /** 8 | * @author: Tomas Zezula 9 | * Date: 27/07/2014 10 | * 11 | * Executes the provided java.lang.Runnable instances (commands) until the MAX_ITERATIONS is reached. 12 | */ 13 | public class TestRunner implements Runnable { 14 | 15 | private TestExecutor testExecutor; 16 | 17 | private Runnable command; 18 | 19 | private int iterations; 20 | 21 | TestRunner(TestExecutor testExecutor, Runnable command) { 22 | this.testExecutor = testExecutor; 23 | this.command = command; 24 | } 25 | 26 | public int getIterations() { 27 | return iterations; 28 | } 29 | 30 | @Override 31 | public void run() { 32 | try { 33 | // Wait for all other threads to start running 34 | testExecutor.getStartSync().await(); 35 | 36 | for (iterations = 0; iterations < testExecutor.getIterations(); iterations++) { 37 | command.run(); 38 | } 39 | 40 | // Notify the main thread the job is done 41 | testExecutor.getStopSync().countDown(); 42 | 43 | } catch (InterruptedException | BrokenBarrierException e) { 44 | fail("Command failed to execute."); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /common/src/test/java/org/zezutom/concurrencypatterns/test/util/StopWatchTest.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.test.util; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.*; 7 | 8 | /** 9 | * @author: Tomas Zezula 10 | * Date: 26/08/2014 11 | */ 12 | public class StopWatchTest { 13 | 14 | public static final long SLEEP_MILLIS = 100L; 15 | 16 | private StopWatch stopWatch; 17 | 18 | @Before 19 | public void init() { 20 | stopWatch = new StopWatch(); 21 | } 22 | 23 | @Test 24 | public void startShouldTriggerTheCount() throws InterruptedException { 25 | 26 | stopWatch.start(); 27 | Thread.sleep(SLEEP_MILLIS); 28 | 29 | long t1 = stopWatch.elapsedTime(); 30 | Thread.sleep(SLEEP_MILLIS); 31 | 32 | long t2 = stopWatch.elapsedTime(); 33 | 34 | assertTrue(t1 > 0); 35 | assertTrue(t1 < t2); 36 | } 37 | 38 | @Test 39 | public void stopShouldHaltTheCount() throws InterruptedException { 40 | 41 | stopWatch.start(); 42 | Thread.sleep(SLEEP_MILLIS); 43 | 44 | long t1 = stopWatch.elapsedTime(); 45 | stopWatch.stop(); 46 | 47 | Thread.sleep(SLEEP_MILLIS); 48 | 49 | long t2 = stopWatch.elapsedTime(); 50 | 51 | assertEquals(t1, t2); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /half-sync-half-async/README.md: -------------------------------------------------------------------------------- 1 | # Half-Sync / Half-Async 2 | 3 | The pattern separates asynchronous I/O from the synchronous one. Main thread doesn't block on incoming client requests and long-running operations are offloaded 4 | to a dedicated synchronous layer. Processing results are delivered by the means of callbacks. 5 | 6 | Examples: 7 | - largely used in operating systems (hardware interrupts, application management) 8 | - Android programming - [AsyncTask](http://developer.android.com/reference/android/os/AsyncTask.html) (file downloads ..) 9 | 10 | A decent queuing system is required to handle messaging between the two layers. The challenge 11 | lies in preventing race conditions and other concurrency related issues. 12 | 13 | ## Key Components 14 | - __Synchronous Service Layer__: deals with long-running tasks, implements the core business logic 15 | - __Queuing Layer__: a request queue, doesn't block the caller 16 | - __Asynchronous Service Layer__: dispatches incoming requests to the queue 17 | 18 | There might be a number of concurrently running synchronous services. The queueing layer 19 | is responsible for thread synchronization. 20 | 21 | ## Pros and Cons 22 | Advantages: 23 | - __Reduced code complexity__: Synchronized services focus solely on the core logic. 24 | - __Separation of concerns__: Each of the layers is relatively self-contained and serves a different purpose. 25 | - __Centralized communication__: The queuing layer mediates all communication - no moving parts flying around 26 | 27 | 28 | Drawbacks: 29 | - __Performance overhead__: "Boundary-crossing penalty" - context switching, synchronization, data transfer .. 30 | - __Harder to debug__: Asynchronous callbacks make testing and debugging less straightforward 31 | - __Benefit questionable__: Higher-level application services may not benefit from asynchronous I/O. That depends on 32 | framework / OS design. 33 | 34 | ## Example 35 | source code directories: 36 | - `src/main/java/org/zezutom/concurrencypatterns/halfsynchalfasync` 37 | - `src/main/java/org/zezutom/concurrencypatterns/halfsynchalfasync/test` 38 | 39 | An ASCII Art generator (credit goes to [Evilzone](https://evilzone.org/java/(java-code)image-to-ascii-art-generator)) 40 | is not only pleasant to work with, but it is also a suitable candidate for a long-running task. I saw it as a perfect 41 | fit for the pattern. 42 | 43 | ```java 44 | public class AsciiArtGenerator { 45 | .. 46 | /** 47 | * Converts an image to its ASCII representation. 48 | * 49 | * @param imgPath path to the image, relative to /src/main/resources 50 | * @param outPath path to the resulting text file, relative to ./data 51 | * @return true, if the conversion succeeds, false otherwise 52 | */ 53 | public boolean convertToAscii(String imgPath, String outPath) {..} 54 | } 55 | ``` 56 | 57 | The image-to-text conversion is a synchronous blocking task, which might take a while 58 | to complete. As such it's bound to run in a background thread. 59 | 60 | The front-end of the app is served asynchronously via a non-blocking dispatcher: 61 | 62 | ```java 63 | /** 64 | * Represents an asynchronous layer, as it forwards client requests for further 65 | * processing and returns immediately. It receives results via notifications. 66 | * 67 | * @author: Tomas Zezula 68 | * Date: 24/08/2014 69 | */ 70 | public class NonBlockingDispatcher { 71 | .. 72 | /** 73 | * Sends a request to the queue and returns instantly. 74 | * 75 | * @param imgPath Image path for the ASCII generator 76 | * @param outPath Output path for the ASCII generator 77 | */ 78 | public void dispatch(final String imgPath, final String outPath) {..} 79 | 80 | /** 81 | * Captures processing result and notifies the subscribed client 82 | * 83 | * @param result true, if success, false otherwise 84 | */ 85 | public void onResult(boolean result) {..} 86 | } 87 | ``` 88 | 89 | Finally, the communication between the dispatcher and the worker thread is mediated by a dedicated queuing channel: 90 | 91 | ```java 92 | /** 93 | * Queues incoming requests and notifies the dispatcher when the response is ready. 94 | * 95 | * @author: Tomas Zezula 96 | * Date: 24/08/2014 97 | */ 98 | public class WorkQueue {..} 99 | ``` 100 | 101 | As usual, the example is accompanied by unit tests proving the core concepts. This time, 102 | I kept the tests to a bare minimum, just to highlight the major difference between a naive 103 | single-threaded synchronous approach and the slightly more advanced asynchronous implementation. 104 | 105 | The app pays tribute to a great actress of the last century. The resulting file (./data/audrey.txt) 106 | is best viewed using a minimal font-size. 107 | 108 | ## Resources 109 | - [The Half-Sync/Half-Async Pattern](http://www.dre.vanderbilt.edu/~schmidt/cs282/PDFs/6-Concurrency-and-Synchronization-part10.pdf) 110 | - [Half-Sync/Half-Async](http://www.cs.wustl.edu/~schmidt/PDF/PLoP-95.pdf) 111 | - [Coursera video lecture: Half-Sync/Half-Async Pattern](https://class.coursera.org/posa-002/lecture/211) 112 | - [Android - AsyncTask](http://developer.android.com/reference/android/os/AsyncTask.html) 113 | - [ASCII Art Generator](https://evilzone.org/java/(java-code)image-to-ascii-art-generator/) 114 | - [Portrait of Audrey Hepburn](http://www.topbesthdpicture.com/wp-content/uploads/2014/05/1152630696_1024x768_audrey-hepburn-230x130.jpg) 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /half-sync-half-async/data/audrey.txt: -------------------------------------------------------------------------------- 1 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@##@@@888####8#8^^^88888&^&88##88@@@@@@@#8^^^^++**..*^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@##@@####@@####8^^88#888&^888##8#@@@@@@@#8^8^8^**+..+#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 3 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@88#@@#####^8&8##8888&888###@@@@@@@#88^&88^^++..^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 4 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#@###@@@#@##8888888888888&####@@@@@@@#88^&^^+..&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 5 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#####@@@@#@@@##88&^8^8&888^###@@@@@@@@##8&^#8^^^^**8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 6 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#@@@@@@@@@@@##@@@@##@@@@@#8^^^^8^8888^^###@#@@@@@@#88&@88++^^**8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 7 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#^^8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#@@@##@@@@###@@@@#8@@@#@@8^^^+^&&8&8&^8#####@@@@@@#88@@#^++^+*+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 8 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#8^^+^8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#@@@#8#@@@####@@###8@@##@#&^+++^&&8&8^^8##@#@@@@@@#888@@@^+***.^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 9 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8^^++**&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#88#@#888####888#@###8&^^++^^8&&&#@###@@@@@@#88@@@#^^*....#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 10 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#8^^+****^8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8888#@#8^&8###8888@##88^^+^++^^^88^^^8#@88#@@@@#8888#@@@&^*....^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 11 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#8^^+*******^8###@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#@@@@@#888###&^^^8#88&^88@##8^^++^+*^888^+^^888^&#@#8888^8&8@@#^^*....^#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 12 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#&^^^++*+***.....*^^##@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@##@@@@88#8#888^^^8888^^&8@##8^^++++++^88^++^&8^^^^##8^^^^^888@@#^+....*^8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 13 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#^^+^++******.....*+^^8##@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#88888&8^^^88888^^8#@##&^+++*+*+^8^+*+^^^^^^^&^^^^++^88#@@8^*...*^#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 14 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8^8#@@@@@@@#^+++++****........+^^^^^8^#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@###88888&^&^^888^&&^8#@##&^**++***+^&**+++^+++^^^^++*+*^&88@#&+....*8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 15 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#+.*^&@@@@@@8^++********........****++^8##@@@#@@@@@@@@@@@@@@@@@@@@@@#888#888^^^^888&^^^^&#@@8^^*..+****^^^***+++*++++^^+**.*+888@8^*...*^#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 16 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#*.*++^@@@@@#^++*+*****............***+^^^^^^8@@@@@@@@@@@@@@@@@@@@##88##8^^^^^^888^^^^^8#@#&^+*..*+*.*+^^*.+*.****++*+++...*+88#@8^*...+8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 17 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^++**+8@@@@#^+*********..............*******+^^8@@@@@@@@##@#@@@8^888##&^^^^^^^88^^^^^&###^^+*...****++*****....*******.....+&8##&^**.*+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 18 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^^+***^@@@@#^+********.......................*^^88#88&88&&8#@8^+^88#8^^^^^^^^^^&^^^^^888^+*......*****..........*****......*^&88^^*...^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 19 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8^^+**+8@@@#^+**********......................*+^^^^^^++^^8&^+*^&88^^^^^^^^^^^^^++^^88^+**......*..*.............****......*^^88^^*...^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 20 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#^^^***&@@@#^++********.........................*********++***+^^^+*++^^+++++^^^^^^^^^**...................................*^^88^+....8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 21 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#^^^+**^@@@#^++**********...................................**+******++*****+^^^^^^^+**.................**.................*^^8^^*...*#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 22 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^^+++*^@@@8^+************...**.**.................................********+++^+++++*................**++++++++**..........*^^8^^*...*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 23 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^+++++^#@@8^+*****+++***++++^^+^^+**................ .... ................*********....... .......*+^^^^^^^^&^^^++*........^^&^+*...+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 24 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8++++*+&@@#^+*****+++++^^888888888^^^+***.......... ............................................*+^^^^&88888888^^^*........^^^^+*...+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 25 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#^+****+#@#^+*****+^^^8##@@@@@@@@@@@#88^^+***......... .... ............................ ....*++^^^888888888&^^+**.........+^^^+**..^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 26 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^+*****^##^******+^^8#@@@@@@@@@@@@@@@@@#&^^+**............. ..............................*^^&888888888888^***............+^^^+*...^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 27 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^******^8#^******+++^8@@@@@@@@@@@@@@@@@@@@#8&^+*........... .. .......................*+^&888######888^+*...............*^^+**...^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 28 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8+*****+&8^**********^^&888###@@@@@@@@@@@@@@@#8^^+*........... ....................*+^^&8###@@@@@@#8&^*.................*+++*....^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 29 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#^*****+^&^+****++******+*++^^^8#@@@@@@@@@@@@@@@#8^+*..............................*+^88@@@@@@@@@@@@#^**..................****.....^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 30 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^*****+^^^+****+++*******....*+^^8#@@@@@@@@@@@@@@@#^^*............ .............*+^8@@@@@@@@@@@@@#8^+**....................*......^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 31 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8+****++^^+*****++*****.......***+^^8@@@@@@@@@@@@@@#&^+............ ...........*+^8#@@@@@@@@@##8&^^++***..........................^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 32 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8+****++^^+************........*****+^^88@@@@@@@@@@@#8^+*.......... ...........+^8#@@@@@@@#88&&&&&^^^^^^+******...................^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 33 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^******+++*******++*****....*****+++++^^^^&8#@@@@@@#8^^+.....................*+^8#@#8#888###@@@@@@@@@@#8^^++***..................^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 34 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^****************++++*******+++^^&8888#888&^^^&88888&^^+**...................*+^^^8^^&8#@@#@@@@@@@@@@@@@#8^^^+**.................^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 35 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^***.*************+++++++*++^8@@@@@@@@@@@@@@#8^^^^^^^^^++**..................*++^^^^^8@@8#@@@@@@@@@@@@@@@@#8^^+*.................8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 36 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&**..**....********++^^&^^^8@@@@@@@@@@@@@@@@@@#8^^+^^^^++***................***+++^^#@#@@@@@@@@@@@@@@@@@@@#88^+..................#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 37 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8+**.......*********+^^##88@@@@@@@@@@@@@@@@@@@@@&^^+++++++**................****++^8@8@@@@@@@@@@@@@@@@@@@@@@8^*.................+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 38 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#^**.......*********++^8@@@@@@@@@@@@@@@@@@@@@@@@@8^+++++++**................*****+^#&@@@@@@@@@@@8*^8@@@@@@@#^+..................^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 39 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^***.......********++^&#@@@@@@@@@8@@@@8@@@@@@@@@@8^++++++**................*****^8^#@8@@#*@#8@@#**+^^#@#8&^*...................8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 40 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^**....**..**********+^^8#@@@@@8^*#@@8^@8#@@^^8@@@8^++++***..................**+^^8#^*#@@@@@@@@#.***+^^^^+*...................*#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 41 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8+**....**.**********++^^^8@@8^***8@@@@@@@@@+**8@@8^^++****...... ........**^+^8^..&###@@@@#^....+&^**.....................^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 42 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^**....********....***++^^88^+*.*^@@@@@@@@8*.*^8##&^^++***..... ........*+*^8^...*8&8###8&....*^^^*......................8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 43 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^***...**..*****......***^^8&^*...8@@@@@@@+...+^^8&^^+****..... .......*++^^+....+&8##8^*....+^^*......................+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 44 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8+**...***.*****........**+^#8^*...^@@@@#^....*+**^^^+****..... .........+*.*.....*^8^^.....*+^*.......................&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 45 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^*.*..*********.........**^^8^^+*..*^^^*....*++++^^+.*.**..... .... ...+++**..... ... ...*+++*.......................*#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 46 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&*.....***.***..........*.*^^^8^^*........**+^^^^^^+*......... ... ...*...............***++*........................+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 47 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#^*........**.............***^^^^^+***.*.***+**+++**.......... .... . ..........***++*+***.........................^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 48 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^*.*......**...............**++^+^^^++++**+*****............. . . .........********...........................8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 49 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#+**.......***..............****++++*******................. ...................... ...................^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 50 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^.........*...................******....................... .................. ...................*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 51 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^*.... ..**.............................................. ........... .................^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 52 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8+**.. ..**................................. ............ .......... .................&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 53 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#+***.....**................................ . ........... .................*#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 54 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^...*....**........................... ... ........ ..............*..^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 55 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8..**.....**..................... ...... ........ ..........******^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 56 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#**.. ...*.**.......... . ........ .........*++^^^^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 57 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^+.. ....****......... . ......... ..........^##@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 58 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&+. ..*******.......... ......... ..........8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 59 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8^+*****.*****............ ......... .........*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 60 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^++++++******............ ........... .........^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 61 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^^^^++.******.......... ............ ..........^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 62 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#888^..*****.......... ............ ...........^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 63 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#^+******.......... ............ ...........&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 64 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8++*****.......... ........... ............8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 65 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^++****.......... ............. .............@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 66 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^+++****......... ............. ..............@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 67 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8+++****.......... .......**..... .. .............+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 68 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8++++***........... ......***.... .... .............^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 69 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@++++++**........... .....****.... ... .............^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 70 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^+++++***.......... .....*****.... .... ..............8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 71 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^+++++***............ .....**.**.... ..... ................#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 72 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8+++++****............ .....**.**.... ..... .................*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 73 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#+*++*****............. . ...***..**..... . . ....... ... ..............+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 74 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^++++*****............. .....**..***..... ... . ....... ...................^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 75 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^++++******.............. .....***..***.... .. .................................8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 76 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&+++********............... ....*.*********.... ......................................*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 77 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#++++********......................*****+++**... . ...*...................................^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 78 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^++++*+*****......................**++^^^^^+*.........*^^^^^^+..............................8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 79 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8+++++++*****......................*^^#@@8^+**.......+^#@@@8^..............................*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 80 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+++++++*****.......................*+^^^^+++***....*^8#888^.........................**....^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 81 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^++*+*+*****.........................*******++*****^8#888^*.........................*.....#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 82 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8+++++++******.........................***.**+^^^+^&###8^*..........................*....*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 83 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^+++++*******..............................*+^^^^^8#8&^*................................&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 84 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8+++++********...............................**+^^&^^*.................................*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 85 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^++++*********..............................*.****....................................&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 86 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8+++++********............................................ ..........................*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 87 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^++++********......................................... ............................8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 88 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#+++++*********.................................... ..............................*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 89 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^+++++*******...*.**............................. . ........****..............*..8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 90 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#+^+++++******..**..*******..*............ . ... ...*+^^^+*............*.*^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 91 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&^+^++*+*****..**.****+++*******............. ........**+^8##^^**.............*#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 92 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^^^++++*****.**.****+^^^^^^^^^^+^+^^+*..... ...**++++^^^&8#8^^+**..........*.*^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 93 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8^^^+++++**********+^&8###8888888888^^^+**.**^^^^^^^&8###8^**+***.......*..**+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 94 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8^+^+++++*********+++++^#@@@@#@@#####88&^^^88#888##@@@8^*.*..............*.*8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 95 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^^^++++++***********..+^8#@@@@@@@@@@@@@##@@@@@@@@@@#&++*.............*..**^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 96 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#^^^+++++++**********.*+^^&&&888888###@@@@@#888888^^++*...............**.+#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 97 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8^^^++++++++**********+^^&^^^^^^^^^^^^^^^^^^^^^+++^+*................***^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 98 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&^^^^++++++***********+^&&&^^^^+++^^^+++***.**+^^++*...............*.**8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 99 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^^^^++++++************^^8888&^^*+++++*******++^^^*.................***^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 100 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^^^^^^+++++***********+^&888&^^+^+++**++++++^^^^+.................**..*8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 101 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#^+^^^^+++++************+^^8#8&^^^++++++++^^^^&^+................****..*^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 102 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&++^^^^+++++*************++^8##88^^^^^^^^^^888^+................****...**8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 103 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#^^++^^^^+++++**************+^^88#888888888#88^*.................***.....*^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 104 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^^^++++^^^++++****************++^^^8888888&^^+*.................***......*^#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 105 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8^^^+++++^^^+++******************+++++++++++*...................***......**+8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 106 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8^^^+*++*+^^^++++**************************....................***.......**+&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 107 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8^^^+*++**+^^^++++***********.................................***.......***+^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 108 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#&^^^++++**+^^^^++***********................................***........*++^8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 109 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8^^^++++++**+^^^+++*******.................................***........**+^^#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 110 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8^^^+++++++**+^^^^+*******................................*+*.........*+^^&#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 111 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#&^^^+++++++**+^^^+++*****................ .............***.........*+^^^8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 112 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8&&^^+++++*****^^^+++****.................. ...........*+*........**+^^^8#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 113 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@###8^^^+++******+^^^++****...........................*++**........*+^^^8#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 114 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#88^+++++*****+^^^++***..........................*+++*........*+^^^8#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 115 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#8^^+++**+****+^^^+****......................*+^^+*.......**+^&88#@@@@@@@@@#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 116 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8^^+++*******+^^^^+****.................**+^^^^*........*+^8##@@@@@@@@@@@@##@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 117 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8^^+++******++^^^^++****.............*^^&88^***..*.***^^8#@@@@@@@@@@@@@@#8#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 118 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#&^^^+*******+^^^^^^+++*********++^^&8###^+*.*...**+^^8#@@@@@@@@@@@@@@@@8^8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 119 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#8^^^++++*++++^^^&^^^^^^^^+^^^^8##@@@#&+**.****++^&##@@@@@@@@@@@@@@@@@8^^8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 120 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#8^^^^+++++^+^^&8##888888#@@@@@@@#8^******+^^&8#@@@@@@@@@@@@@@@@@@@#&^^#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 121 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@88&^^^^^^^^^^888####@@@@@@@@@#^+***++^^^8#@@@@@@@@@@@@@@@@@@@@@#8^^8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 122 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@##8888###@@@@@@@@@@@@@8^+*++^^88#@@@@@@@@@@@@@@@@@@@@@@@@#&^^8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 123 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8^+^^8##@@@@@@@@@@@@@@@@@@@@@@@@@@@@#88&8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 124 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8^^8#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#&^&#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 125 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&^#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#8^^^#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 126 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8&#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#8^^^^8#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 127 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@88@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#88^^^^^8#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 128 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8&#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#88^^^^^^8#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 129 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8#^8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#8^^^^^^^^&8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 130 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#@@@@@@@@&888@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#8^^^^^^^^^^^8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 131 | -------------------------------------------------------------------------------- /half-sync-half-async/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | concurrency-patterns 7 | org.zezutom 8 | ${my.version} 9 | 10 | 4.0.0 11 | 12 | half-sync-half-async 13 | 14 | 15 | 16 | org.zezutom 17 | common 18 | ${my.version} 19 | 20 | 21 | 22 | commons-io 23 | commons-io 24 | 2.4 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /half-sync-half-async/src/main/java/org/zezutom/concurrencypatterns/halfsynchalfasync/AsciiArtGenerator.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.halfsynchalfasync; 2 | 3 | import javax.imageio.ImageIO; 4 | import java.awt.*; 5 | import java.awt.image.BufferedImage; 6 | import java.io.File; 7 | import java.io.FileWriter; 8 | import java.io.IOException; 9 | import java.io.PrintWriter; 10 | import java.net.URISyntaxException; 11 | import java.net.URL; 12 | import java.nio.file.Files; 13 | import java.nio.file.Path; 14 | import java.nio.file.Paths; 15 | 16 | /** 17 | * Credit goes to https://evilzone.org/java/(java-code)image-to-ascii-art-generator/ 18 | * 19 | * @author: Tomas Zezula 20 | * Date: 27/08/2014 21 | */ 22 | public class AsciiArtGenerator { 23 | 24 | public static final double RED = 0.30; 25 | 26 | public static final double GREEN = 0.11; 27 | 28 | public static final double BLUE = 0.59; 29 | 30 | private BufferedImage img; 31 | 32 | double pixval; 33 | 34 | private PrintWriter printWriter; 35 | 36 | private FileWriter fileWriter; 37 | 38 | private int[] gradients = new int[]{240, 210, 190, 170, 120, 110, 80, 60}; 39 | 40 | private char[] chars = new char[]{' ', '.', '*', '+', '^', '&', '8', '#', '@'}; 41 | 42 | /** 43 | * Converts an image to its ASCII representation. 44 | * 45 | * @param imgPath path to the image, relative to /src/main/resources 46 | * @param outPath path to the resulting text file, relative to ./data 47 | * @return true, if the conversion succeeds, false otherwise 48 | */ 49 | public boolean convertToAscii(String imgPath, String outPath) { 50 | try { 51 | printWriter = new PrintWriter(fileWriter = new FileWriter(outPath)); 52 | Path filePath = Paths.get(imgPath); 53 | if (Files.exists(filePath)) { 54 | img = ImageIO.read(filePath.toFile()); 55 | } else { 56 | File resource = getResource(imgPath); 57 | if (resource != null && resource.isFile()) { 58 | img = ImageIO.read(resource); 59 | } else { 60 | throw new IllegalArgumentException(String.format("File '%s' couldn't be found. Does it exist?", imgPath)); 61 | } 62 | } 63 | } catch (IOException | URISyntaxException e) { 64 | throw new RuntimeException("The image couldn't be created: " + e); 65 | } 66 | 67 | 68 | for (int i = 0; i < img.getHeight(); i++) { 69 | for (int j = 0; j < img.getWidth(); j++) { 70 | Color pixcol = new Color(img.getRGB(j, i)); 71 | pixval = (((pixcol.getRed() * RED) + (pixcol.getBlue() * BLUE) + (pixcol.getGreen() * GREEN))); 72 | print(px2Char(pixval)); 73 | } 74 | try { 75 | printWriter.println(""); 76 | printWriter.flush(); 77 | fileWriter.flush(); 78 | } catch (Exception e) { 79 | throw new RuntimeException("The awesome ASCII art couldn't be saved: " + e); 80 | } 81 | } 82 | return true; 83 | } 84 | 85 | private File getResource(String filename) throws URISyntaxException { 86 | URL resource = getClass().getResource("/" + filename); 87 | return (resource == null) ? null : new File(resource.toURI()); 88 | } 89 | 90 | 91 | public char px2Char(double px) { 92 | Character c = null; 93 | 94 | for (int i = 0; i < gradients.length; i++) { 95 | if (px >= gradients[i]) { 96 | c = chars[i]; 97 | break; 98 | } 99 | } 100 | 101 | if (c == null) { 102 | c = chars[chars.length - 1]; 103 | } 104 | return c; 105 | } 106 | 107 | 108 | public void print(char c) { 109 | try { 110 | printWriter.print(c); 111 | printWriter.flush(); 112 | fileWriter.flush(); 113 | } catch (Exception e) { 114 | throw new RuntimeException("Something went wrong when saving to disc: " + e); 115 | } 116 | } 117 | 118 | public static void main(String[] args) { 119 | AsciiArtGenerator art = new AsciiArtGenerator(); 120 | art.convertToAscii("audrey_hepburn.jpg", "audrey.txt"); 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /half-sync-half-async/src/main/java/org/zezutom/concurrencypatterns/halfsynchalfasync/BlockingDispatcher.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.halfsynchalfasync; 2 | 3 | /** 4 | * A usual single-threaded implementation. 5 | * 6 | * @author: Tomas Zezula 7 | * Date: 24/08/2014 8 | */ 9 | public class BlockingDispatcher { 10 | 11 | /** 12 | * Takes an image (jpg, png) and converts it to an ASCII representation. 13 | * 14 | * @param imgPath This takes either an absolute path to an image (JPG, PNG) 15 | * or a relative path to a classpath resources (src/main/resources) 16 | * @param outPath Path to the output txt file 17 | * @return True if the conversion has been successful, false otherwise. 18 | */ 19 | public boolean convertToAscii(String imgPath, String outPath) { 20 | return new AsciiArtGenerator().convertToAscii(imgPath, outPath); 21 | } 22 | 23 | public static void main(String[] args) { 24 | boolean result = new BlockingDispatcher().convertToAscii("audrey_hepburn.jpg", "half-sync-half-async/data/audrey.txt"); 25 | System.out.println("RESULT: " + result); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /half-sync-half-async/src/main/java/org/zezutom/concurrencypatterns/halfsynchalfasync/NonBlockingDispatcher.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.halfsynchalfasync; 2 | 3 | /** 4 | * Represents an asynchronous layer, as it forwards client requests for further 5 | * processing and returns immediately. It receives results via notifications. 6 | * 7 | * @author: Tomas Zezula 8 | * Date: 24/08/2014 9 | */ 10 | public class NonBlockingDispatcher { 11 | 12 | private boolean result = false; 13 | 14 | private WorkQueue queue; 15 | 16 | private ResultSubscriber subscriber; 17 | 18 | public NonBlockingDispatcher(ResultSubscriber subscriber) { 19 | queue = new WorkQueue(this); 20 | this.subscriber = subscriber; 21 | } 22 | 23 | /** 24 | * Sends a request to the queue and returns instantly. 25 | * 26 | * @param imgPath Image path for the ASCII generator 27 | * @param outPath Output path for the ASCII generator 28 | */ 29 | public void dispatch(final String imgPath, final String outPath) { 30 | Runnable submission = new Runnable() { 31 | @Override 32 | public void run() { 33 | queue.submit(imgPath, outPath); 34 | } 35 | }; 36 | new Thread(submission).start(); 37 | } 38 | 39 | /** 40 | * Captures processing result and notifies the subscribed client 41 | * 42 | * @param result true, if success, false otherwise 43 | */ 44 | public void onResult(boolean result) { 45 | if (subscriber != null) { 46 | subscriber.onResult(result); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /half-sync-half-async/src/main/java/org/zezutom/concurrencypatterns/halfsynchalfasync/ResultSubscriber.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.halfsynchalfasync; 2 | 3 | /** 4 | * @author: Tomas Zezula 5 | * Date: 24/08/2014 6 | */ 7 | public interface ResultSubscriber { 8 | 9 | void onResult(boolean result); 10 | } 11 | -------------------------------------------------------------------------------- /half-sync-half-async/src/main/java/org/zezutom/concurrencypatterns/halfsynchalfasync/WorkQueue.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.halfsynchalfasync; 2 | 3 | import java.util.concurrent.*; 4 | 5 | /** 6 | * Queues incoming requests and notifies the dispatcher when the response is ready. 7 | * 8 | * @author: Tomas Zezula 9 | * Date: 24/08/2014 10 | */ 11 | public class WorkQueue { 12 | 13 | // Activation List: incoming requests (tasks) are put into a queue 14 | private volatile BlockingQueue> taskQueue = new LinkedBlockingQueue<>(); 15 | 16 | public WorkQueue(final NonBlockingDispatcher dispatcher) { 17 | new Thread(new Runnable() { 18 | @Override 19 | public void run() { 20 | while(taskQueue.isEmpty()) 21 | ; // await tasks 22 | 23 | // This is the actual task scheduler. It only allows for a single task at a time. 24 | ExecutorService executorService = Executors.newSingleThreadExecutor(); 25 | 26 | try { 27 | while (true) { 28 | if (taskQueue.isEmpty()) break; 29 | 30 | // at some point in the future the calculated value will be available 31 | Future future = executorService.submit(taskQueue.take()); 32 | while (!future.isDone()) 33 | ; // wait until the calculation is complete 34 | 35 | // publish the result 36 | dispatcher.onResult(future.get()); 37 | 38 | } 39 | } catch (InterruptedException | ExecutionException e) { 40 | throw new RuntimeException("Task execution was failed!"); 41 | } 42 | finally { 43 | executorService.shutdown(); 44 | } 45 | } 46 | }).start(); 47 | 48 | } 49 | 50 | public void submit(String imgPath, String outPath) { 51 | submit(createTask(imgPath, outPath)); 52 | } 53 | 54 | private void submit(Callable task) { 55 | try { 56 | taskQueue.put(task); 57 | } catch (InterruptedException e) { 58 | throw new RuntimeException("Task scheduling was interrupted!"); 59 | } 60 | } 61 | 62 | private Callable createTask(final String imgPath, final String outPath) { 63 | return new Callable() { 64 | @Override 65 | public Boolean call() throws Exception { 66 | return new AsciiArtGenerator().convertToAscii(imgPath, outPath); 67 | } 68 | }; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /half-sync-half-async/src/main/resources/audrey_hepburn.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zezutom/concurrency-patterns/ec637b2ef896914717f365428504ff900161f2b5/half-sync-half-async/src/main/resources/audrey_hepburn.jpg -------------------------------------------------------------------------------- /half-sync-half-async/src/test/java/org/zezutom/concurrencypatterns/halfsynchalfasync/test/AsyncResultSubscriber.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.halfsynchalfasync.test; 2 | 3 | import org.zezutom.concurrencypatterns.halfsynchalfasync.NonBlockingDispatcher; 4 | import org.zezutom.concurrencypatterns.halfsynchalfasync.ResultSubscriber; 5 | 6 | /** 7 | * @author: Tomas Zezula 8 | * Date: 24/08/2014 9 | */ 10 | public class AsyncResultSubscriber implements Runnable, ResultSubscriber { 11 | 12 | private boolean result; 13 | 14 | private NonBlockingDispatcher app; 15 | 16 | private long callTime; 17 | 18 | private long responseTime; 19 | 20 | private String imgPath, outPath; 21 | 22 | public AsyncResultSubscriber(String imgPath, String outPath) { 23 | this.imgPath = imgPath; 24 | this.outPath = outPath; 25 | app = new NonBlockingDispatcher(this); 26 | } 27 | 28 | @Override 29 | public void onResult(boolean result) { 30 | responseTime = System.currentTimeMillis(); 31 | this.result = result; 32 | } 33 | 34 | @Override 35 | public void run() { 36 | app.dispatch(imgPath, outPath); 37 | callTime = System.currentTimeMillis(); 38 | try { 39 | Thread.sleep(1000L); 40 | } catch (InterruptedException e) { 41 | throw new RuntimeException("Execution interrupted!"); 42 | } 43 | } 44 | 45 | public boolean getResult() { 46 | return result; 47 | } 48 | 49 | public boolean isAsynchronous() { 50 | return callTime <= responseTime; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /half-sync-half-async/src/test/java/org/zezutom/concurrencypatterns/halfsynchalfasync/test/BlockingDispatcherTest.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.halfsynchalfasync.test; 2 | 3 | import org.apache.commons.io.FileUtils; 4 | import org.junit.After; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | import org.zezutom.concurrencypatterns.halfsynchalfasync.BlockingDispatcher; 8 | import org.zezutom.concurrencypatterns.halfsynchalfasync.BlockingDispatcher; 9 | import org.zezutom.concurrencypatterns.test.util.DataUtil; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | 14 | import static org.junit.Assert.assertEquals; 15 | import static org.junit.Assert.assertTrue; 16 | 17 | /** 18 | * @author: Tomas Zezula 19 | * Date: 24/08/2014 20 | */ 21 | public class BlockingDispatcherTest { 22 | 23 | public static final String IMAGE = "audrey_hepburn.jpeg"; 24 | 25 | public static final String OUT_TEST = "audrey-test.txt"; 26 | 27 | public static final String OUT_ORIGINAL = "audrey.txt"; 28 | 29 | private BlockingDispatcher app = new BlockingDispatcher(); 30 | 31 | @After 32 | public void cleanUp() { 33 | final File asciiFile = DataUtil.getFile(OUT_TEST); 34 | if (asciiFile.exists()) asciiFile.delete(); 35 | } 36 | 37 | @Test 38 | public void asciiArtRocks() throws IOException { 39 | assertTrue(app.convertToAscii(IMAGE, OUT_TEST)); 40 | assertTrue(FileUtils.contentEquals(DataUtil.getFile(OUT_ORIGINAL), DataUtil.getFile(OUT_TEST))); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /half-sync-half-async/src/test/java/org/zezutom/concurrencypatterns/halfsynchalfasync/test/NonBlockingDispatcherTest.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.halfsynchalfasync.test; 2 | 3 | import org.apache.commons.io.FileUtils; 4 | import org.junit.After; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | import org.zezutom.concurrencypatterns.test.util.DataUtil; 8 | import org.zezutom.concurrencypatterns.test.util.TestExecutor; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | 13 | import static org.junit.Assert.assertTrue; 14 | /** 15 | * @author: Tomas Zezula 16 | * Date: 24/08/2014 17 | */ 18 | public class NonBlockingDispatcherTest { 19 | 20 | public static final String IMAGE = "audrey_hepburn.jpeg"; 21 | 22 | public static final String OUT_TEST = "audrey-test.txt"; 23 | 24 | public static final String OUT_ORIGINAL = "audrey.txt"; 25 | 26 | private AsyncResultSubscriber subscriber; 27 | 28 | @Before 29 | public void init() { 30 | subscriber = new AsyncResultSubscriber(IMAGE, OUT_TEST); 31 | } 32 | 33 | @After 34 | public void cleanUp() { 35 | final File asciiFile = DataUtil.getFile(OUT_TEST); 36 | if (asciiFile.exists()) asciiFile.delete(); 37 | } 38 | 39 | @Test 40 | public void asyncAsciiArtRocks() throws IOException { 41 | TestExecutor.getSingle().runTest(subscriber); 42 | assertTrue(subscriber.getResult()); 43 | assertTrue(FileUtils.contentEquals(DataUtil.getFile(OUT_ORIGINAL), DataUtil.getFile(OUT_TEST))); 44 | assertTrue(subscriber.isAsynchronous()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /monitor-object/README.md: -------------------------------------------------------------------------------- 1 | # Monitor Object 2 | 3 | The pattern revolves around synchronization. In short, concurrent threads (clients) can only use the object via 4 | a set of synchronized methods. Only one method can run at a time. Typically a synchronized method watches for a 5 | certain condition. However, there is no polling involved. Instead, the methods are being notified. 6 | That's a crucial difference in comparison to the [Active Object](../active-object). 7 | 8 | Monitor Object is similar to the [Active Object](../active-object) in a sense that it exposes a defined interface via object's synchronized methods. 9 | On the other hand, the methods execute in the client's thread as there is no notion of a centralized thread control. There is no significant performance overhead either, Since inefficient busy-waiting loops (polling) are replaced with notifications. 10 | 11 | ## Key Components 12 | 13 | - __Monitor Object__: exposes synchronized methods as the only means of client access 14 | - __Synchronized Methods__: guarantee thread-safe access to the internal state 15 | - __Monitor Lock__: used by the synchronized methods to serialize method invocation 16 | - __Monitor Condition__: caters for cooperative execution scheduling 17 | 18 | ## Pros and Cons 19 | 20 | Advantages: 21 | - __Simplified synchronization__: All of the hard work is offloaded to the object itself, clients are not concerned with synchronization issues. 22 | - __Cooperative execution scheduling__: Monitor conditions are used to suspend / resume method execution. 23 | - __Reduced performance overhead__: Notifications over inefficient polling. 24 | 25 | Drawbacks: 26 | - __Synchronization tightly coupled with core logic__: Synchronization code blends into the business logic which breaks the principle of separation of concerns. 27 | - __Nested monitor lockout problem__: An endless wait for a condition to become true can occur when a monitor object is nested into another kind of its own. 28 | In Java for example, monitor locks are not shared between two separate classes. Thus, it can happen that the outer monitor is never released and any threads 29 | watching that monitor would be kept waiting. 30 | 31 | ## Example 32 | source code directories: 33 | - `src/main/java/org/zezutom/concurrencypatterns/monitorobject` 34 | - `src/main/java/org/zezutom/concurrencypatterns/monitorobject/test` 35 | 36 | Inspired by a blog post [Java Monitor Pattern](http://www.e-zest.net/blog/java-monitor-pattern) 37 | I came up with a hypothetical usage of a public toilet. Not only does it reminiscence of what makes 38 | us all equal, but it also comprises pattern's dominant attributes. A toilet is either occupied or vacant, 39 | hence the locked / unlocked parallel. It also should only be used by a single person at a time (race conditions). 40 | 41 | Only a vacant toilet can be entered. Once in, the visitor is granted to leave: 42 | ```java 43 | public interface Toilet { 44 | 45 | boolean enter(); 46 | 47 | void quit(); 48 | 49 | boolean isOccupied(); 50 | } 51 | ``` 52 | 53 | Now, the challenge is to ensure, under any circumstances, that the toilet only be used by a single person. 54 | Should that condition fail, the toilet becomes flooded: 55 | 56 | ```java 57 | public class ToiletFloodedException extends RuntimeException {} 58 | ``` 59 | 60 | An obviously ignorant implementation of the Toilet interface ..: 61 | 62 | ```java 63 | public class FilthyToilet implements Toilet { 64 | 65 | // Yes, that's the internal state - a concurrent visitor counter. 66 | private int counter = 0; 67 | .. 68 | } 69 | ``` 70 | 71 | .. has unavoidable consequences: console output after having run the `FilthyToiletMultiThreadedTest.java`: 72 | ```java 73 | The toilet was flooded 25 times under a moderate load. 74 | The toilet was flooded 38 times under a heavy load. 75 | The toilet was flooded 96 times under an extreme load. 76 | ``` 77 | 78 | Please note that the flood-count varies and doesn't always follow the load. 79 | 80 | Now, the correct implementation makes use of the Monitor Object pattern: 81 | 82 | ```java 83 | public class CleanToilet implements Toilet { 84 | 85 | // Monitor Lock used by the synchronized methods 86 | private final ReentrantLock lock; 87 | 88 | // Monitor Condition - the toilet can only be used by a single person at a time 89 | private Condition oneAtATimeCondition; 90 | 91 | // The guarded object's state - the 'volatile' flag is crucial for the signalling to work 92 | private volatile int counter; 93 | 94 | // all of the public methods are synchronized 95 | .. 96 | } 97 | ``` 98 | The synchronization is ensured by using a lock along with a condition. The lock holds as long as the condition holds true: 99 | 100 | ```java 101 | public boolean enter() { 102 | lock.lock(); 103 | try { 104 | while (counter > 0) { // wait while the toilet is being used 105 | oneAtATimeCondition.awaitUninterruptibly(); 106 | } 107 | 108 | if (++counter == 1) { 109 | oneAtATimeCondition.signal(); // the toilet has been successfully acquired 110 | } 111 | 112 | return isOccupied(); 113 | } finally { 114 | lock.unlock(); 115 | } 116 | } 117 | ``` 118 | 119 | ## Resources 120 | - [Wikipedia](http://en.wikipedia.org/wiki/Monitor_(synchronization)) 121 | - [Monitor Object: An Object Behavioral Pattern for Concurrent Programming](http://www.cs.wustl.edu/~schmidt/PDF/monitor.pdf) 122 | - [Monitor Object Concurrency Pattern](http://www.mijnadres.net/published/Monitor%20Object%20Pattern.pdf) 123 | - [Java Monitor Pattern](http://www.e-zest.net/blog/java-monitor-pattern) 124 | -------------------------------------------------------------------------------- /monitor-object/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | concurrency-patterns 7 | org.zezutom 8 | ${my.version} 9 | 10 | 4.0.0 11 | 12 | monitor-object 13 | 14 | 15 | 16 | org.zezutom 17 | common 18 | ${my.version} 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /monitor-object/src/main/java/org/zezutom/concurrencypatterns/monitorobject/CleanToilet.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.monitorobject; 2 | 3 | import java.util.concurrent.locks.Condition; 4 | import java.util.concurrent.locks.ReentrantLock; 5 | 6 | /** 7 | * @author Tomas Zezula 8 | * Date: 27/07/2014 9 | */ 10 | public class CleanToilet implements Toilet { 11 | 12 | // Monitor Lock used by the synchronized methods 13 | private final ReentrantLock lock; 14 | 15 | // Monitor Condition - the toilet can only be used by a single person at a time 16 | private Condition oneAtATimeCondition; 17 | 18 | // The guarded object's state - the 'volatile' flag is crucial for the signaling to work 19 | private volatile int counter; 20 | 21 | public CleanToilet() { 22 | lock = new ReentrantLock(true); 23 | oneAtATimeCondition = lock.newCondition(); 24 | } 25 | 26 | @Override 27 | public boolean enter() { 28 | lock.lock(); 29 | try { 30 | while (counter > 0) { // wait while the toilet is being used 31 | oneAtATimeCondition.awaitUninterruptibly(); 32 | } 33 | 34 | if (++counter == 1) { 35 | oneAtATimeCondition.signal(); // the toilet has been successfully acquired 36 | } 37 | 38 | return isOccupied(); 39 | } finally { 40 | lock.unlock(); 41 | } 42 | } 43 | 44 | @Override 45 | public void quit() { 46 | lock.lock(); 47 | try { 48 | 49 | while (counter <= 0) { // leaving a vacant toilet makes no sense 50 | oneAtATimeCondition.awaitUninterruptibly(); 51 | } 52 | 53 | if(isOccupied()) { 54 | if (--counter == 0) { 55 | oneAtATimeCondition.signal(); // the toilet is free to use from this point on 56 | } 57 | } 58 | } finally { 59 | lock.unlock(); 60 | } 61 | } 62 | 63 | @Override 64 | public boolean isOccupied() { 65 | if (counter < 0 || counter > 1) { 66 | throw new ToiletFloodedException(); 67 | } 68 | return counter > 0; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /monitor-object/src/main/java/org/zezutom/concurrencypatterns/monitorobject/FilthyToilet.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.monitorobject; 2 | 3 | /** 4 | * @author Tomas Zezula 5 | * Date: 27/07/2014 6 | * 7 | */ 8 | public class FilthyToilet implements Toilet { 9 | 10 | private int counter = 0; 11 | 12 | @Override 13 | public boolean enter() { 14 | if (!isOccupied()) { 15 | counter++; 16 | } 17 | return isOccupied(); 18 | } 19 | 20 | @Override 21 | public void quit() { 22 | counter--; 23 | } 24 | 25 | @Override 26 | public boolean isOccupied() { 27 | if (counter < 0 || counter > 1) { 28 | throw new ToiletFloodedException(); 29 | } 30 | return counter > 0; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /monitor-object/src/main/java/org/zezutom/concurrencypatterns/monitorobject/Toilet.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.monitorobject; 2 | 3 | /** 4 | * @author Tomas Zezula 5 | * Date: 27/07/2014 6 | * 7 | */ 8 | public interface Toilet { 9 | 10 | boolean enter(); 11 | 12 | void quit(); 13 | 14 | boolean isOccupied(); 15 | } 16 | -------------------------------------------------------------------------------- /monitor-object/src/main/java/org/zezutom/concurrencypatterns/monitorobject/ToiletFloodedException.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.monitorobject; 2 | 3 | /** 4 | * @author Tomas Zezula 5 | * Date: 27/07/2014 6 | */ 7 | public class ToiletFloodedException extends RuntimeException { 8 | } 9 | -------------------------------------------------------------------------------- /monitor-object/src/test/java/org/zezutom/concurrencypatterns/monitorobject/test/CleanToiletMultiThreadedTest.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.monitorobject.test; 2 | 3 | import org.junit.Before; 4 | import org.junit.BeforeClass; 5 | import org.junit.Test; 6 | import org.zezutom.concurrencypatterns.monitorobject.CleanToilet; 7 | import org.zezutom.concurrencypatterns.monitorobject.Toilet; 8 | import org.zezutom.concurrencypatterns.monitorobject.ToiletFloodedException; 9 | import org.zezutom.concurrencypatterns.test.util.TestExecutor; 10 | 11 | import static org.junit.Assert.assertTrue; 12 | 13 | /** 14 | * @author Tomas Zezula 15 | * Date: 27/07/2014 16 | */ 17 | public class CleanToiletMultiThreadedTest { 18 | 19 | public static final long MIN_WAIT_MILLIS = 100; 20 | 21 | public static final long MAX_WAIT_MILLIS = 120; 22 | 23 | private static volatile Toilet toilet; 24 | 25 | private static Runnable oneTimePatron; 26 | 27 | private static Runnable peacefulMind; 28 | 29 | private static Runnable frequentFlier; 30 | 31 | private static int toiletFloodedCount; 32 | 33 | @BeforeClass 34 | public static void setUp() { 35 | 36 | oneTimePatron = new Runnable() { 37 | @Override 38 | public void run() { 39 | setUsageTest(100, 1); 40 | } 41 | }; 42 | 43 | peacefulMind = new Runnable() { 44 | @Override 45 | public void run() { 46 | setUsageTest(MAX_WAIT_MILLIS, 1); 47 | } 48 | }; 49 | 50 | frequentFlier = new Runnable() { 51 | @Override 52 | public void run() { 53 | setUsageTest(105, 3); 54 | } 55 | }; 56 | } 57 | 58 | private static void setUsageTest(long acquireMillis, int visitCount) { 59 | for (int i = 0; i < visitCount; i++) { 60 | try { 61 | if (toilet.enter()) { 62 | Thread.sleep(acquireMillis); 63 | toilet.quit(); 64 | } 65 | } catch (InterruptedException e) { 66 | // Don't bother 67 | } catch (ToiletFloodedException e) { 68 | toiletFloodedCount++; 69 | } 70 | 71 | // Another round ahead? 72 | if (i < visitCount - 1) { 73 | final long waitPeriod = (long) (Math.random() * (MAX_WAIT_MILLIS - MIN_WAIT_MILLIS) + MIN_WAIT_MILLIS); 74 | try { 75 | Thread.sleep(waitPeriod); 76 | } catch (InterruptedException e) { 77 | // No worries 78 | } 79 | } 80 | } 81 | } 82 | 83 | @Before 84 | public void init() { 85 | toiletFloodedCount = 0; 86 | toilet = new CleanToilet(); 87 | } 88 | 89 | @Test 90 | public void testRegularTraffic() { 91 | TestExecutor.get(10, 5).runTest(oneTimePatron); 92 | assertIncidents(); 93 | } 94 | 95 | @Test 96 | public void testPeakHour() { 97 | TestExecutor.get(10, 15).runTest(oneTimePatron, peacefulMind); 98 | assertIncidents(); 99 | } 100 | 101 | @Test 102 | public void testBusyBeyondBelief() { 103 | TestExecutor.get(10, 25).runTest(oneTimePatron, peacefulMind, frequentFlier); 104 | assertIncidents(); 105 | } 106 | 107 | private void assertIncidents() { 108 | assertTrue("The toilet was flooded " + toiletFloodedCount + " times!", toiletFloodedCount == 0); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /monitor-object/src/test/java/org/zezutom/concurrencypatterns/monitorobject/test/CleanToiletSingleThreadedTest.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.monitorobject.test; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import org.zezutom.concurrencypatterns.monitorobject.CleanToilet; 6 | import org.zezutom.concurrencypatterns.monitorobject.Toilet; 7 | import org.zezutom.concurrencypatterns.monitorobject.ToiletFloodedException; 8 | 9 | import static org.junit.Assert.assertFalse; 10 | import static org.junit.Assert.assertTrue; 11 | 12 | /** 13 | * @author Tomas Zezula 14 | * Date: 27/07/2014 15 | * 16 | */ 17 | public class CleanToiletSingleThreadedTest { 18 | 19 | private Toilet toilet; 20 | 21 | @Before 22 | public void init() { 23 | toilet = new CleanToilet(); 24 | } 25 | 26 | @Test 27 | public void acquireVacantToilet() throws ToiletFloodedException { 28 | assertTrue(toilet.enter()); 29 | assertTrue(toilet.isOccupied()); 30 | } 31 | 32 | @Test 33 | public void leaveOccupiedToilet() throws ToiletFloodedException { 34 | toilet.enter(); 35 | toilet.quit(); 36 | assertFalse(toilet.isOccupied()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /monitor-object/src/test/java/org/zezutom/concurrencypatterns/monitorobject/test/FilthyToiletMultiThreadedTest.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.monitorobject.test; 2 | 3 | import org.junit.Before; 4 | import org.junit.BeforeClass; 5 | import org.junit.Test; 6 | import org.zezutom.concurrencypatterns.monitorobject.FilthyToilet; 7 | import org.zezutom.concurrencypatterns.monitorobject.Toilet; 8 | import org.zezutom.concurrencypatterns.monitorobject.ToiletFloodedException; 9 | import org.zezutom.concurrencypatterns.test.util.TestExecutor; 10 | 11 | import static org.junit.Assert.assertTrue; 12 | 13 | /** 14 | * @author Tomas Zezula 15 | * Date: 27/07/2014 16 | */ 17 | public class FilthyToiletMultiThreadedTest { 18 | 19 | public static final long MIN_WAIT_MILLIS = 100; 20 | 21 | public static final long MAX_WAIT_MILLIS = 120; 22 | 23 | private static volatile Toilet toilet; 24 | 25 | private static Runnable oneTimePatron; 26 | 27 | private static Runnable peacefulMind; 28 | 29 | private static Runnable frequentFlier; 30 | 31 | private static int toiletFloodedCount; 32 | 33 | private enum Load { 34 | MODERATE("a moderate"), 35 | HEAVY("a heavy"), 36 | EXTREME("an extreme"); 37 | 38 | private String value; 39 | 40 | private Load(String value) { 41 | this.value = value; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return value; 47 | } 48 | } 49 | 50 | @BeforeClass 51 | public static void setUp() { 52 | 53 | oneTimePatron = new Runnable() { 54 | @Override 55 | public void run() { 56 | setUsageTest(100, 1); 57 | } 58 | }; 59 | 60 | peacefulMind = new Runnable() { 61 | @Override 62 | public void run() { 63 | setUsageTest(MAX_WAIT_MILLIS, 1); 64 | } 65 | }; 66 | 67 | frequentFlier = new Runnable() { 68 | @Override 69 | public void run() { 70 | setUsageTest(105, 3); 71 | } 72 | }; 73 | } 74 | 75 | private static void setUsageTest(long acquireMillis, int visitCount) { 76 | for (int i = 0; i < visitCount; i++) { 77 | try { 78 | if (toilet.enter()) { 79 | Thread.sleep(acquireMillis); 80 | toilet.quit(); 81 | } 82 | } catch (InterruptedException e) { 83 | // Don't bother 84 | } catch (ToiletFloodedException e) { 85 | toiletFloodedCount++; 86 | } 87 | 88 | // Another round ahead? 89 | if (i < visitCount - 1) { 90 | final long waitPeriod = (long) (Math.random() * (MAX_WAIT_MILLIS - MIN_WAIT_MILLIS) + MIN_WAIT_MILLIS); 91 | try { 92 | Thread.sleep(waitPeriod); 93 | } catch (InterruptedException e) { 94 | // No worries 95 | } 96 | } 97 | } 98 | } 99 | 100 | @Before 101 | public void init() { 102 | toiletFloodedCount = 0; 103 | toilet = new FilthyToilet(); 104 | } 105 | 106 | @Test 107 | public void testRegularTraffic() { 108 | runTest(Load.MODERATE, TestExecutor.get(10, 5), oneTimePatron); 109 | } 110 | 111 | @Test 112 | public void testPeakHour() { 113 | runTest(Load.HEAVY, TestExecutor.get(10, 15), frequentFlier, peacefulMind); 114 | } 115 | 116 | @Test 117 | public void testBusyBeyondBelief() { 118 | runTest(Load.EXTREME, TestExecutor.get(20, 25), oneTimePatron, peacefulMind, frequentFlier); 119 | } 120 | 121 | private void runTest(Load load, TestExecutor executor, Runnable ... clients) { 122 | executor.runTest(clients); 123 | assertIncidents(load); 124 | } 125 | 126 | private void assertIncidents(Load load) { 127 | System.out.println(String.format("The toilet was flooded %d times under %s load.", toiletFloodedCount, load)); 128 | assertTrue("The toilet was unexpectedly clean.", toiletFloodedCount > 0); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /monitor-object/src/test/java/org/zezutom/concurrencypatterns/monitorobject/test/FilthyToiletSingleThreadedTest.java: -------------------------------------------------------------------------------- 1 | package org.zezutom.concurrencypatterns.monitorobject.test; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import org.zezutom.concurrencypatterns.monitorobject.FilthyToilet; 6 | import org.zezutom.concurrencypatterns.monitorobject.Toilet; 7 | import org.zezutom.concurrencypatterns.monitorobject.ToiletFloodedException; 8 | 9 | import static org.junit.Assert.assertFalse; 10 | import static org.junit.Assert.assertTrue; 11 | 12 | /** 13 | * @author Tomas Zezula 14 | * Date: 27/07/2014 15 | * 16 | */ 17 | public class FilthyToiletSingleThreadedTest { 18 | 19 | private Toilet toilet; 20 | 21 | @Before 22 | public void init() { 23 | toilet = new FilthyToilet(); 24 | } 25 | 26 | @Test 27 | public void acquireVacantToilet() throws ToiletFloodedException { 28 | assertTrue(toilet.enter()); 29 | assertTrue(toilet.isOccupied()); 30 | } 31 | 32 | @Test 33 | public void leaveOccupiedToilet() throws ToiletFloodedException { 34 | toilet.enter(); 35 | toilet.quit(); 36 | assertFalse(toilet.isOccupied()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | 1.0-SNAPSHOT 9 | 10 | 11 | org.zezutom 12 | concurrency-patterns 13 | pom 14 | ${my.version} 15 | 16 | common 17 | active-object 18 | monitor-object 19 | half-sync-half-async 20 | 21 | 22 | 23 | 24 | junit 25 | junit 26 | 4.10 27 | 28 | 29 | 30 | 31 | 32 | 33 | org.apache.maven.plugins 34 | maven-compiler-plugin 35 | 3.1 36 | 37 | 1.7 38 | 1.7 39 | 40 | 41 | 42 | 43 | 44 | --------------------------------------------------------------------------------