diff --git a/join/README.md b/join/README.md new file mode 100644 index 000000000000..8b47d0b35490 --- /dev/null +++ b/join/README.md @@ -0,0 +1,135 @@ +--- +title: "Join Pattern in Java: Streamlining Concurrent Operations" +shortTitle: Join +description: "Master the Join Design Pattern in Java to coordinate and synchronize concurrent tasks effectively. Explore examples, code implementations, benefits, and practical applications." +category: Concurrency +language: en +tag: + - Concurrency + - Synchronization + - Parallel processing + - Gang of Four +--- + +## Also known as + +* Fork-Join Pattern + +## Intent of Join Pattern + +The Join Pattern in Java focuses on coordinating and synchronizing concurrent tasks to achieve a specific outcome. It ensures that multiple tasks can execute independently, and their results are merged once all tasks complete. + +## Detailed Explanation of Join Pattern with Real-World Examples + +Real-world example + +> Imagine a multi-chef kitchen preparing different dishes for a single order. Each chef works independently on their assigned dish, but the order cannot be delivered until every dish is ready. The kitchen manager, acting as the join point, ensures synchronization and prepares the final order once all dishes are done. Similarly, the Join Pattern allows tasks to execute concurrently and synchronizes their results for a final outcome. + +In plain words + +> The Join Pattern helps in synchronizing multiple independent tasks, allowing them to work concurrently and combining their outcomes efficiently. + +Wikipedia says + +> The join design pattern is a parallel processing pattern that helps merge results of concurrently executed tasks. + +## Programmatic Example of Join Pattern in Java + +In this example, we demonstrate how the Join Pattern can be implemented to manage multiple threads and synchronize their results. We use a task aggregator that collects data from individual tasks and combines it into a final result. + +```java + +package com.iluwatar.join; + +import lombok.extern.slf4j.Slf4j; + +/** Here main thread will execute after completion of 4 demo threads + * main thread will continue when CountDownLatch count becomes 0 + * CountDownLatch will start with count 4 and 4 demo threads will decrease it by 1 + * everytime when they will finish . + */ + +@Slf4j +public class JoinPatternDemo { + + /** + * execution of demo and dependent threads. + */ + public static void main(String[] args) { + + int[] executionOrder = {4, 2, 1, 3}; + int noOfDemoThreads = 4; + int noOfDependentThreads = 2; + JoinPattern pattern = new JoinPattern(noOfDemoThreads, executionOrder); + Thread previous = null; + + for (int i = 0; i < noOfDemoThreads; i++) { + previous = new Thread(new DemoThread(executionOrder[i], previous)); + previous.start(); + } + pattern.await(); + + //Dependent threads after execution of DemoThreads + for (int i = 0; i < noOfDependentThreads; i++) { + new DependentThread(i + 1).start(); + } + LOGGER.info("end of program "); + + } + +} + +``` + +### Program Output: + +``` +Running com.iluwatar.join.JoinPatternTest +01:13:17.890 [Thread-2] INFO com.iluwatar.join.DemoThread -- Thread 1 starts +01:13:18.167 [Thread-2] INFO com.iluwatar.join.DemoThread -- Thread 1 ends +01:13:18.168 [Thread-3] INFO com.iluwatar.join.DemoThread -- Thread 4 starts +01:13:19.176 [Thread-3] INFO com.iluwatar.join.DemoThread -- Thread 4 ends +01:13:19.176 [Thread-4] INFO com.iluwatar.join.DemoThread -- Thread 3 starts +01:13:19.935 [Thread-4] INFO com.iluwatar.join.DemoThread -- Thread 3 ends +01:13:19.935 [Thread-5] INFO com.iluwatar.join.DemoThread -- Thread 2 starts +01:13:20.437 [Thread-5] INFO com.iluwatar.join.DemoThread -- Thread 2 ends +``` + +## When to Use the Join Pattern in Java + +Use the Join Pattern in Java: + +* To synchronize results from multiple independent tasks executing in parallel. +* To aggregate and process data from various sources concurrently. +* To reduce the complexity of managing multiple threads in parallel operations. + +## Real-World Applications of Join Pattern in Java + +* Managing concurrent HTTP requests and aggregating their responses into a single result. +* Parallel processing of large datasets, such as in map-reduce frameworks. +* Synchronizing asynchronous operations, e.g., CompletableFutures in Java. + +## Benefits and Trade-offs of Join Pattern + +### Benefits: + +* Efficiently handles parallel processing tasks with minimal synchronization overhead. +* Improves application performance by utilizing available system resources optimally. +* Simplifies the logic for managing and synchronizing multiple tasks. + +### Trade-offs: + +* Debugging can become challenging with large numbers of asynchronous tasks. +* Improper use may lead to deadlocks or performance bottlenecks. + +## Related Java Design Patterns + +* [Fork-Join Framework](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinPool.html): Built-in Java framework for recursive task splitting and joining. +* [Future and CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html): Used for handling and synchronizing asynchronous operations. +* [Observer Pattern](https://java-design-patterns.com/patterns/observer/): Can be combined with Join to monitor task progress. + +## References and Credits + +* [Java Concurrency in Practice](https://amzn.to/3sfS8mT) +* [Effective Java](https://amzn.to/3GxS8p4) +* [Oracle Java Documentation on Concurrency](https://docs.oracle.com/javase/tutorial/essential/concurrency/) diff --git a/join/etc/JoinPatternFlowDiagram.png b/join/etc/JoinPatternFlowDiagram.png new file mode 100644 index 000000000000..3add159e0f1b Binary files /dev/null and b/join/etc/JoinPatternFlowDiagram.png differ diff --git a/join/etc/java-join-method.png b/join/etc/java-join-method.png new file mode 100644 index 000000000000..792d0f324610 Binary files /dev/null and b/join/etc/java-join-method.png differ diff --git a/join/etc/join.urm.puml b/join/etc/join.urm.puml new file mode 100644 index 000000000000..64a17f9451a0 --- /dev/null +++ b/join/etc/join.urm.puml @@ -0,0 +1,37 @@ +@startuml +package com.iluwatar.join { + class DemoThread { + - LOGGER : Logger {static} + - actualExecutionOrder : int[] {static} + - executionOrder : int[] {static} + - id : int + - index : int {static} + - pattern : JoinPattern {static} + - previous : Thread + + DemoThread(id : int, previous : Thread) + + getActualExecutionOrder() : int[] {static} + + run() + + setExecutionOrder(executionOrder : int[], pattern : JoinPattern) {static} + } + class DependentThread { + - LOGGER : Logger {static} + - id : int + ~ DependentThread(id : int) + + run() + } + class JoinPattern { + ~ executionOrder : int[] + - latch : CountDownLatch + ~ noOfDemoThreads : int + + JoinPattern(noOfDemoThreads : int, executionOrder : int[]) + + await() + + countdown() + } + class JoinPatternDemo { + - LOGGER : Logger {static} + + JoinPatternDemo() + + main(String[]) {static} + } +} +DemoThread --> "-pattern" JoinPattern +@enduml \ No newline at end of file diff --git a/join/pom.xml b/join/pom.xml new file mode 100644 index 000000000000..5dc154c8e71a --- /dev/null +++ b/join/pom.xml @@ -0,0 +1,72 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + + join + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.mockito + mockito-core + test + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.join.JoinPatternDemo + + + + + + + + + diff --git a/join/src/main/java/com/iluwatar/join/DemoThread.java b/join/src/main/java/com/iluwatar/join/DemoThread.java new file mode 100644 index 000000000000..2fe714288cbb --- /dev/null +++ b/join/src/main/java/com/iluwatar/join/DemoThread.java @@ -0,0 +1,85 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.join; + +import lombok.extern.slf4j.Slf4j; + +/** + * demo threads implementing Runnable . + */ +@Slf4j +public class DemoThread implements Runnable { + + private static int[] executionOrder; + private static int[] actualExecutionOrder; + private static int index = 0; + private static JoinPattern pattern; + private int id; + private Thread previous; + + /** + * Initalise a demo thread object with id and previous thread . + */ + public DemoThread(int id, Thread previous) { + this.id = id; + this.previous = previous; + + } + + public static int[] getActualExecutionOrder() { + return actualExecutionOrder; + } + /** + * set custom execution order of threads . + */ + public static void setExecutionOrder(int[] executionOrder, JoinPattern pattern) { + DemoThread.executionOrder = executionOrder; + DemoThread.pattern = pattern; + actualExecutionOrder = new int[executionOrder.length]; + } + /** + * use to run demo thread. + */ + public void run() { + if (previous != null) { + try { + previous.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + LOGGER.info("Thread " + id + " starts"); + try { + Thread.sleep(id * 250); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + LOGGER.info("Thread " + id + " ends"); + actualExecutionOrder[index++] = id; + pattern.countdown(); + } + } + +} diff --git a/join/src/main/java/com/iluwatar/join/DependentThread.java b/join/src/main/java/com/iluwatar/join/DependentThread.java new file mode 100644 index 000000000000..3bd1abbd0b8a --- /dev/null +++ b/join/src/main/java/com/iluwatar/join/DependentThread.java @@ -0,0 +1,57 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.join; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * Dependent threads will execute only after completion of all demothreads. + */ +@Slf4j +public class DependentThread implements Runnable { + + + private int id; + DependentThread(int id) { + this.id = id; + } + /** + * dependent threads run . + */ + public void run() { + + LOGGER.info(" Dependent Thread " + id + " starts "); + try { + Thread.sleep(id * 200); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + LOGGER.info("Dependent Thread " + id + " completed "); + } + + } +} diff --git a/join/src/main/java/com/iluwatar/join/JoinPattern.java b/join/src/main/java/com/iluwatar/join/JoinPattern.java new file mode 100644 index 000000000000..934f1930d9e5 --- /dev/null +++ b/join/src/main/java/com/iluwatar/join/JoinPattern.java @@ -0,0 +1,65 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.join; + +import java.util.concurrent.CountDownLatch; + +/** + * The Join design pattern allows multiple concurrent processes or threads to be + * synchronized such that they all must complete before any subsequent tasks can + * proceed. This pattern is particularly useful in scenarios where tasks can be + * executed in parallel but the subsequent tasks must wait for the completion of + * these parallel tasks. + */ +public class JoinPattern { + + int noOfDemoThreads; + private CountDownLatch latch; + int[] executionOrder; + + /** + * Initialise join pattern object. + */ + public JoinPattern(int noOfDemoThreads, int[] executionOrder) { + latch = new CountDownLatch(noOfDemoThreads); + this.executionOrder = executionOrder; + DemoThread.setExecutionOrder(executionOrder, this); + } + + /** + * decreases count by one. + */ + public void countdown() { + latch.countDown(); + } + + /** + * thread waits until count reaches 0. + */ + public void await() throws InterruptedException { + latch.await(); + } + +} diff --git a/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java b/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java new file mode 100644 index 000000000000..7030208fb5ef --- /dev/null +++ b/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java @@ -0,0 +1,63 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.join; + +import lombok.extern.slf4j.Slf4j; + +/** Here main thread will execute after completion of 4 demo threads + * main thread will continue when CountDownLatch count becomes 0 + * CountDownLatch will start with count 4 and 4 demo threads will decrease it by 1 + * everytime when they will finish . + */ + +@Slf4j +public class JoinPatternDemo { + + /** + * execution of demo and dependent threads. + */ + public static void main(String[] args) throws InterruptedException { + + int[] executionOrder = {4, 2, 1, 3}; + int noOfDemoThreads = 4; + int noOfDependentThreads = 2; + JoinPattern pattern = new JoinPattern(noOfDemoThreads, executionOrder); + Thread previous = null; + + for (int i = 0; i < noOfDemoThreads; i++) { + previous = new Thread(new DemoThread(executionOrder[i], previous)); + previous.start(); + } + pattern.await(); + + //Dependent threads after execution of DemoThreads + for (int i = 0; i < noOfDependentThreads; i++) { + new Thread(new DependentThread(i + 1)).start(); + } + LOGGER.info("end of program "); + + } + +} diff --git a/join/src/test/java/com/iluwatar/join/JoinPatternTest.java b/join/src/test/java/com/iluwatar/join/JoinPatternTest.java new file mode 100644 index 000000000000..de581c3bb65a --- /dev/null +++ b/join/src/test/java/com/iluwatar/join/JoinPatternTest.java @@ -0,0 +1,53 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.join; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +public class JoinPatternTest { + + @Test + public void test() throws InterruptedException { + + int noOfDemoThreads = 4; + int[] executionOrder = {1, 4, 3, 2}; + JoinPattern pattern = new JoinPattern(noOfDemoThreads, executionOrder); + + Thread previous = null; + + for (int i = 0; i < noOfDemoThreads; i++) { + previous = new Thread(new DemoThread(executionOrder[i], previous)); + previous.start(); + + } + pattern.await(); + + int[] actualExecutionOrder = DemoThread.getActualExecutionOrder(); + + assertArrayEquals(executionOrder, actualExecutionOrder, "Demo threads did not execute in given order "); + + } +} diff --git a/pom.xml b/pom.xml index 5cc512b7de94..4bca110b0d68 100644 --- a/pom.xml +++ b/pom.xml @@ -78,6 +78,7 @@ command interpreter iterator + join mediator memento model-view-presenter