From cf93fc89a08c5049a29ee97b5023c4b9400f42f0 Mon Sep 17 00:00:00 2001 From: KeshavMM Date: Sun, 8 Dec 2024 20:29:56 +0530 Subject: [PATCH 1/6] Added Join Design Pattern Issue #70 --- join/README.md | 119 ++++++++++++++++++ join/etc/JoinPatternFlowDiagram.png | Bin 0 -> 38179 bytes join/etc/java-join-method.png | Bin 0 -> 11818 bytes join/pom.xml | 72 +++++++++++ .../java/com/iluwatar/join/DemoThread.java | 80 ++++++++++++ .../com/iluwatar/join/DependentThread.java | 55 ++++++++ .../java/com/iluwatar/join/JoinPattern.java | 55 ++++++++ .../com/iluwatar/join/JoinPatternDemo.java | 60 +++++++++ .../com/iluwatar/join/JoinPatternTest.java | 53 ++++++++ pom.xml | 1 + 10 files changed, 495 insertions(+) create mode 100644 join/README.md create mode 100644 join/etc/JoinPatternFlowDiagram.png create mode 100644 join/etc/java-join-method.png create mode 100644 join/pom.xml create mode 100644 join/src/main/java/com/iluwatar/join/DemoThread.java create mode 100644 join/src/main/java/com/iluwatar/join/DependentThread.java create mode 100644 join/src/main/java/com/iluwatar/join/JoinPattern.java create mode 100644 join/src/main/java/com/iluwatar/join/JoinPatternDemo.java create mode 100644 join/src/test/java/com/iluwatar/join/JoinPatternTest.java diff --git a/join/README.md b/join/README.md new file mode 100644 index 000000000000..d1a5fb118518 --- /dev/null +++ b/join/README.md @@ -0,0 +1,119 @@ +--- +title: "Join Pattern in Java: Synchronizing Concurrent Tasks" +shortTitle: Join +description: "Learn the Join Design Pattern in Java with detailed examples and explanations. Understand how to synchronize concurrent tasks and manage execution flow using the Join Pattern. Ideal for developers looking to improve their multithreading and synchronization skills." +category: Behavioral +language: en +issue: #70 +tag: + - Concurrency + - Synchronization + - Threads + - Multithreading + - Parallel Execution +--- + +## Intent of Join Design Pattern + +The **Join Design Pattern** in Java is used to synchronize multiple concurrent processes or threads so that they must all complete before any subsequent tasks can proceed. This pattern is essential when tasks are executed in parallel, but the subsequent tasks need to wait until all parallel tasks are finished. It allows threads to "join" at a synchronization point and ensures correct execution order and timing. + +## Detailed Explanation of Join Pattern with Real-World Examples + +#### Real-World Example + +Imagine a **construction project** where multiple contractors are working on different aspects of the building simultaneously. The project manager doesn't want to proceed with the final inspection of the building until all the contractors have finished their respective tasks. Using the **Join Design Pattern**, the manager waits for all contractors (threads) to complete their work before proceeding with the inspection (subsequent task). + +This pattern allows the project manager to synchronize all contractors' tasks to ensure that the inspection is only performed once all work is completed. + +#### Wikipedia Definition: + +> "Join is a synchronization technique that allows multiple concurrent threads or processes to synchronize and wait for the completion of other threads before proceeding to subsequent tasks." + +## Programmatic Example of Join Pattern in Java + +In this example, we simulate a scenario where four demo tasks run concurrently, and the main thread waits for their completion before proceeding. This is achieved using the **Thread#join()** method, which ensures that the main thread waits for all demo tasks to finish before continuing. + +### DemoThreadClass + +```java +/* + * 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; + +/* + * DemoThreads 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; + + public DemoThread(int id, Thread previous) { + this.id = id; + this.previous = previous; + + } + + public static int[] getActualExecutionOrder() { + return actualExecutionOrder; + } + + public static void setExecutionOrder(int[] executionOrder, JoinPattern pattern) { + DemoThread.executionOrder = executionOrder; + DemoThread.pattern = pattern; + actualExecutionOrder = new int[executionOrder.length]; + } + + 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/etc/JoinPatternFlowDiagram.png b/join/etc/JoinPatternFlowDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..3add159e0f1b24f199f48d39a2a1e7f177b3ff53 GIT binary patch literal 38179 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 F{4eC5@-+Ye literal 0 HcmV?d00001 diff --git a/join/etc/java-join-method.png b/join/etc/java-join-method.png new file mode 100644 index 0000000000000000000000000000000000000000..792d0f32461098e46376dca5e1d3d103dfff3147 GIT binary patch literal 11818 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 literal 0 HcmV?d00001 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..5250b0d5f5b1 --- /dev/null +++ b/join/src/main/java/com/iluwatar/join/DemoThread.java @@ -0,0 +1,80 @@ +/* + * 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; + +/* + * DemoThreads 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; + + public DemoThread(int id, Thread previous) { + this.id = id; + this.previous = previous; + + } + + public static int[] getActualExecutionOrder() { + return actualExecutionOrder; + } + + public static void setExecutionOrder(int[] executionOrder, JoinPattern pattern) { + DemoThread.executionOrder = executionOrder; + DemoThread.pattern = pattern; + actualExecutionOrder = new int[executionOrder.length]; + } + + 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..78a08c42386a --- /dev/null +++ b/join/src/main/java/com/iluwatar/join/DependentThread.java @@ -0,0 +1,55 @@ +/* + * 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 { + + private int id; + + DependentThread(int id) { + this.id = id; + } + + 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..d67bbb56b266 --- /dev/null +++ b/join/src/main/java/com/iluwatar/join/JoinPattern.java @@ -0,0 +1,55 @@ +/* + * 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; + + public JoinPattern(int noOfDemoThreads, int[] executionOrder) { + latch = new CountDownLatch(noOfDemoThreads); + this.executionOrder = executionOrder; + } + + public void countdown() { + latch.countDown(); + } + + 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..63db8861690d --- /dev/null +++ b/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java @@ -0,0 +1,60 @@ +/* + * 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 { + + 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 "); + + } + +} 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 5c8dee618606..3e6bd4dffce7 100644 --- a/pom.xml +++ b/pom.xml @@ -78,6 +78,7 @@ command interpreter iterator + join mediator memento model-view-presenter From 3e23c5ec79a4dbb453441816b7ca42ecf6893054 Mon Sep 17 00:00:00 2001 From: KeshavMM Date: Tue, 7 Jan 2025 23:32:07 +0530 Subject: [PATCH 2/6] resolved checkstyle violations --- .../java/com/iluwatar/join/DemoThread.java | 89 ++++++++++--------- .../com/iluwatar/join/DependentThread.java | 36 ++++---- .../java/com/iluwatar/join/JoinPattern.java | 26 +++--- .../com/iluwatar/join/JoinPatternDemo.java | 41 +++++---- 4 files changed, 101 insertions(+), 91 deletions(-) diff --git a/join/src/main/java/com/iluwatar/join/DemoThread.java b/join/src/main/java/com/iluwatar/join/DemoThread.java index 5250b0d5f5b1..d7279bda8c20 100644 --- a/join/src/main/java/com/iluwatar/join/DemoThread.java +++ b/join/src/main/java/com/iluwatar/join/DemoThread.java @@ -26,55 +26,60 @@ import lombok.extern.slf4j.Slf4j; -/* - * DemoThreads implementing Runnable +/** + * 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; - - public DemoThread(int id, Thread previous) { - this.id = id; - this.previous = previous; - - } + 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; - } + } - public static void setExecutionOrder(int[] executionOrder, JoinPattern pattern) { - DemoThread.executionOrder = executionOrder; - DemoThread.pattern = pattern; - actualExecutionOrder = new int[executionOrder.length]; + 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(); + } } - - 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(); - - } + 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 index 78a08c42386a..e048280c7ec6 100644 --- a/join/src/main/java/com/iluwatar/join/DependentThread.java +++ b/join/src/main/java/com/iluwatar/join/DependentThread.java @@ -28,28 +28,30 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -/* - * Dependent threads will execute only after completion of all demothreads +/** + * Dependent threads will execute only after completion of all demothreads. */ @Slf4j public class DependentThread { - private int id; + + private int id; + DependentThread(int id) { + this.id = id; + } + /** + * dependent threads run . + */ + public void run() { - DependentThread(int id) { - this.id = id; + Logger.info(" Dependent Thread " + id + " starts "); + try { + Thread.sleep(id * 200); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + Logger.info("Dependent Thread " + id + " completed "); } - 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 index d67bbb56b266..68eec8179797 100644 --- a/join/src/main/java/com/iluwatar/join/JoinPattern.java +++ b/join/src/main/java/com/iluwatar/join/JoinPattern.java @@ -35,21 +35,21 @@ */ public class JoinPattern { - int noOfDemoThreads; - private CountDownLatch latch; - int[] executionOrder; + int noOfDemoThreads; + private CountDownLatch latch; + int[] executionOrder; - public JoinPattern(int noOfDemoThreads, int[] executionOrder) { - latch = new CountDownLatch(noOfDemoThreads); - this.executionOrder = executionOrder; - } + public JoinPattern(int noOfDemoThreads, int[] executionOrder) { + latch = new CountDownLatch(noOfDemoThreads); + this.executionOrder = executionOrder; + } - public void countdown() { - latch.countDown(); - } + public void countdown() { + latch.countDown(); + } - public void await() throws InterruptedException { - latch.await(); - } + 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 index 63db8861690d..5a42861aad20 100644 --- a/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java +++ b/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java @@ -26,35 +26,38 @@ import lombok.extern.slf4j.Slf4j; -/* Here main thread will execute after completion of 4 demo threads +/** 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 { - 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(); + /** + * execution of demo and dependent threads. + */ + public static void main(String[] args) { - } - pattern.await(); + int[] executionOrder = {4, 2, 1, 3}; + int noOfDemoThreads = 4; + int noOfDependentThreads = 2; + JoinPattern pattern = new JoinPattern(noOfDemoThreads, executionOrder); + Thread previous = null; - //Dependent threads after execution of DemoThreads - for (int i = 0; i < noOfDependentThreads; i++) { - new DependentThread(i + 1).start(); - } - Logger.info("end of program "); + 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 "); + + } } From 3d474c46e1fe10c0196d428b2f7ccb33cfc5617a Mon Sep 17 00:00:00 2001 From: KeshavMM Date: Wed, 8 Jan 2025 01:17:25 +0530 Subject: [PATCH 3/6] resolved checkstyle conflicts --- join/README.md | 184 ++++++++++-------- join/etc/join.urm.puml | 37 ++++ .../java/com/iluwatar/join/DemoThread.java | 4 +- .../com/iluwatar/join/DependentThread.java | 6 +- .../java/com/iluwatar/join/JoinPattern.java | 12 +- .../com/iluwatar/join/JoinPatternDemo.java | 2 +- 6 files changed, 154 insertions(+), 91 deletions(-) create mode 100644 join/etc/join.urm.puml diff --git a/join/README.md b/join/README.md index d1a5fb118518..8b47d0b35490 100644 --- a/join/README.md +++ b/join/README.md @@ -1,119 +1,135 @@ --- -title: "Join Pattern in Java: Synchronizing Concurrent Tasks" +title: "Join Pattern in Java: Streamlining Concurrent Operations" shortTitle: Join -description: "Learn the Join Design Pattern in Java with detailed examples and explanations. Understand how to synchronize concurrent tasks and manage execution flow using the Join Pattern. Ideal for developers looking to improve their multithreading and synchronization skills." -category: Behavioral +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 -issue: #70 tag: - Concurrency - Synchronization - - Threads - - Multithreading - - Parallel Execution + - Parallel processing + - Gang of Four --- -## Intent of Join Design Pattern +## Also known as -The **Join Design Pattern** in Java is used to synchronize multiple concurrent processes or threads so that they must all complete before any subsequent tasks can proceed. This pattern is essential when tasks are executed in parallel, but the subsequent tasks need to wait until all parallel tasks are finished. It allows threads to "join" at a synchronization point and ensures correct execution order and timing. +* 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 +Real-world example -Imagine a **construction project** where multiple contractors are working on different aspects of the building simultaneously. The project manager doesn't want to proceed with the final inspection of the building until all the contractors have finished their respective tasks. Using the **Join Design Pattern**, the manager waits for all contractors (threads) to complete their work before proceeding with the inspection (subsequent task). +> 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. -This pattern allows the project manager to synchronize all contractors' tasks to ensure that the inspection is only performed once all work is completed. +In plain words -#### Wikipedia Definition: +> The Join Pattern helps in synchronizing multiple independent tasks, allowing them to work concurrently and combining their outcomes efficiently. -> "Join is a synchronization technique that allows multiple concurrent threads or processes to synchronize and wait for the completion of other threads before proceeding to subsequent tasks." +Wikipedia says -## Programmatic Example of Join Pattern in Java +> The join design pattern is a parallel processing pattern that helps merge results of concurrently executed tasks. -In this example, we simulate a scenario where four demo tasks run concurrently, and the main thread waits for their completion before proceeding. This is achieved using the **Thread#join()** method, which ensures that the main thread waits for all demo tasks to finish before continuing. +## Programmatic Example of Join Pattern in Java -### DemoThreadClass +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 -/* - * 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; -/* - * DemoThreads implementing Runnable +/** 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 DemoThread implements Runnable { +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(); - private static int[] executionOrder; - private static int[] actualExecutionOrder; - private static int index = 0; - private static JoinPattern pattern; - private int id; - private Thread previous; + //Dependent threads after execution of DemoThreads + for (int i = 0; i < noOfDependentThreads; i++) { + new DependentThread(i + 1).start(); + } + LOGGER.info("end of program "); - public DemoThread(int id, Thread previous) { - this.id = id; - this.previous = previous; + } - } +} - public static int[] getActualExecutionOrder() { - return actualExecutionOrder; - } +``` - public static void setExecutionOrder(int[] executionOrder, JoinPattern pattern) { - DemoThread.executionOrder = executionOrder; - DemoThread.pattern = pattern; - actualExecutionOrder = new int[executionOrder.length]; - } +### Program Output: - 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(); - - } - } +``` +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/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/src/main/java/com/iluwatar/join/DemoThread.java b/join/src/main/java/com/iluwatar/join/DemoThread.java index d7279bda8c20..2fe714288cbb 100644 --- a/join/src/main/java/com/iluwatar/join/DemoThread.java +++ b/join/src/main/java/com/iluwatar/join/DemoThread.java @@ -70,13 +70,13 @@ public void run() { e.printStackTrace(); } } - Logger.info("Thread " + id + " starts"); + LOGGER.info("Thread " + id + " starts"); try { Thread.sleep(id * 250); } catch (InterruptedException e) { e.printStackTrace(); } finally { - Logger.info("Thread " + id + " ends"); + 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 index e048280c7ec6..3bd1abbd0b8a 100644 --- a/join/src/main/java/com/iluwatar/join/DependentThread.java +++ b/join/src/main/java/com/iluwatar/join/DependentThread.java @@ -32,7 +32,7 @@ * Dependent threads will execute only after completion of all demothreads. */ @Slf4j -public class DependentThread { +public class DependentThread implements Runnable { private int id; @@ -44,13 +44,13 @@ public class DependentThread { */ public void run() { - Logger.info(" Dependent Thread " + id + " starts "); + LOGGER.info(" Dependent Thread " + id + " starts "); try { Thread.sleep(id * 200); } catch (InterruptedException e) { e.printStackTrace(); } finally { - Logger.info("Dependent Thread " + id + " completed "); + 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 index 68eec8179797..934f1930d9e5 100644 --- a/join/src/main/java/com/iluwatar/join/JoinPattern.java +++ b/join/src/main/java/com/iluwatar/join/JoinPattern.java @@ -39,15 +39,25 @@ public class JoinPattern { 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 index 5a42861aad20..4d7df82851d9 100644 --- a/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java +++ b/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java @@ -56,7 +56,7 @@ public static void main(String[] args) { for (int i = 0; i < noOfDependentThreads; i++) { new DependentThread(i + 1).start(); } - Logger.info("end of program "); + LOGGER.info("end of program "); } From 1277bfcf935e45ee2eae39ec2e86c1b66bd2b199 Mon Sep 17 00:00:00 2001 From: KeshavMM Date: Wed, 8 Jan 2025 01:32:22 +0530 Subject: [PATCH 4/6] corrected thread creation --- join/src/main/java/com/iluwatar/join/JoinPatternDemo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java b/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java index 4d7df82851d9..1c1c306584a0 100644 --- a/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java +++ b/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java @@ -54,7 +54,7 @@ public static void main(String[] args) { //Dependent threads after execution of DemoThreads for (int i = 0; i < noOfDependentThreads; i++) { - new DependentThread(i + 1).start(); + new Thread(new DependentThread(i + 1)).start(); } LOGGER.info("end of program "); From 0b11c314c2e4d5dbf9c189ab289ddc0ada8300be Mon Sep 17 00:00:00 2001 From: KeshavMM Date: Wed, 8 Jan 2025 01:39:01 +0530 Subject: [PATCH 5/6] thrown exception --- join/src/main/java/com/iluwatar/join/JoinPatternDemo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java b/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java index 1c1c306584a0..7030208fb5ef 100644 --- a/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java +++ b/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java @@ -38,7 +38,7 @@ public class JoinPatternDemo { /** * execution of demo and dependent threads. */ - public static void main(String[] args) { + public static void main(String[] args) throws InterruptedException { int[] executionOrder = {4, 2, 1, 3}; int noOfDemoThreads = 4; From 3b4bc80a4ef778ff6676b86e70ee5af073662bef Mon Sep 17 00:00:00 2001 From: KeshavMM Date: Thu, 16 Jan 2025 19:43:42 +0530 Subject: [PATCH 6/6] updated README.MD --- join/README.md | 71 ++++++++++--------- .../java/com/iluwatar/join/DemoThread.java | 20 +++--- .../com/iluwatar/join/DependentThread.java | 11 ++- .../com/iluwatar/join/JoinPatternDemo.java | 8 +++ 4 files changed, 64 insertions(+), 46 deletions(-) diff --git a/join/README.md b/join/README.md index 8b47d0b35490..3cba7f198818 100644 --- a/join/README.md +++ b/join/README.md @@ -47,26 +47,15 @@ import lombok.extern.slf4j.Slf4j; * 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 . + * DemoThreads are implemented in join pattern such that every newly created thread + * waits for the completion of previous thread by previous.join() . Hence maintaining + * execution order of demo threads . + * JoinPattern object ensures that dependent threads execute only after completion of + * demo threads by pattern.await() . This method keep the main thread in waiting state + * until countdown latch becomes 0 . CountdownLatch will become 0 as all demo threads + * will be completed as each of them have decreased it by 1 and its initial count was set to noOfDemoThreads. + * Hence this pattern ensures dependent threads will start only after completion of demo threads . */ - -@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 @@ -75,25 +64,43 @@ public class JoinPatternDemo { } LOGGER.info("end of program "); - } + /** + * use to run demo thread. + * every newly created thread waits for + * the completion of previous thread + * by previous.join() . + */ + @Override + public void run() { + if (previous != null) { + try { + previous.join(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + LOGGER.error("Interrupted exception : ", e); + } + } -} ``` ### 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 -``` +[INFO] Running com.iluwatar.join.JoinPatternTest +16:12:01.815 [Thread-2] INFO com.iluwatar.join.DemoThread -- Thread 1 starts +16:12:02.086 [Thread-2] INFO com.iluwatar.join.DemoThread -- Thread 1 ends +16:12:02.087 [Thread-3] INFO com.iluwatar.join.DemoThread -- Thread 4 starts +16:12:03.090 [Thread-3] INFO com.iluwatar.join.DemoThread -- Thread 4 ends +16:12:03.091 [Thread-4] INFO com.iluwatar.join.DemoThread -- Thread 3 starts +16:12:03.851 [Thread-4] INFO com.iluwatar.join.DemoThread -- Thread 3 ends +16:12:03.851 [Thread-5] INFO com.iluwatar.join.DemoThread -- Thread 2 starts +16:12:04.352 [Thread-5] INFO com.iluwatar.join.DemoThread -- Thread 2 ends +[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.904 s -- in com.iluwatar.join.JoinPatternTest +[INFO] +[INFO] Results: +[INFO] +[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 +[INFO] ## When to Use the Join Pattern in Java diff --git a/join/src/main/java/com/iluwatar/join/DemoThread.java b/join/src/main/java/com/iluwatar/join/DemoThread.java index 2fe714288cbb..63d391c0aeea 100644 --- a/join/src/main/java/com/iluwatar/join/DemoThread.java +++ b/join/src/main/java/com/iluwatar/join/DemoThread.java @@ -32,15 +32,14 @@ @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; + private final int id; + private final Thread previous; /** - * Initalise a demo thread object with id and previous thread . + * Initialise a demo thread object with id and previous thread . */ public DemoThread(int id, Thread previous) { this.id = id; @@ -55,26 +54,31 @@ public static int[] getActualExecutionOrder() { * 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. + * every newly created thread waits for + * the completion of previous thread + * by previous.join() . */ + @Override public void run() { if (previous != null) { try { previous.join(); } catch (InterruptedException e) { - e.printStackTrace(); + Thread.currentThread().interrupt(); + LOGGER.error("Interrupted exception : ", e); } } LOGGER.info("Thread " + id + " starts"); try { - Thread.sleep(id * 250); + Thread.sleep(id * (long) 250); } catch (InterruptedException e) { - e.printStackTrace(); + Thread.currentThread().interrupt(); + LOGGER.error("Interrupted exception : ", e); } finally { LOGGER.info("Thread " + id + " ends"); actualExecutionOrder[index++] = id; diff --git a/join/src/main/java/com/iluwatar/join/DependentThread.java b/join/src/main/java/com/iluwatar/join/DependentThread.java index 3bd1abbd0b8a..a089abf23ee5 100644 --- a/join/src/main/java/com/iluwatar/join/DependentThread.java +++ b/join/src/main/java/com/iluwatar/join/DependentThread.java @@ -24,8 +24,6 @@ */ package com.iluwatar.join; -import lombok.Getter; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; /** @@ -34,21 +32,22 @@ @Slf4j public class DependentThread implements Runnable { - - private int id; + private final int id; DependentThread(int id) { this.id = id; } /** * dependent threads run . */ + @Override public void run() { LOGGER.info(" Dependent Thread " + id + " starts "); try { - Thread.sleep(id * 200); + Thread.sleep(id * (long) 200); } catch (InterruptedException e) { - e.printStackTrace(); + Thread.currentThread().interrupt(); + LOGGER.error("Interrupted exception : ", e); } finally { LOGGER.info("Dependent Thread " + id + " completed "); } diff --git a/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java b/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java index 7030208fb5ef..e8b64541bfed 100644 --- a/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java +++ b/join/src/main/java/com/iluwatar/join/JoinPatternDemo.java @@ -30,6 +30,14 @@ * 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 . + * DemoThreads are implemented in join pattern such that every newly created thread + * waits for the completion of previous thread by previous.join() . Hence maintaining + * execution order of demo threads . + * JoinPattern object ensures that dependent threads execute only after completion + * demo threads by pattern.await() . This method keep the main thread in waiting state + * until countdown latch becomes 0 . CountdownLatch will become 0 as all demo threads + * will be completed as each of them have decreased it by 1 and its initial count was set to noOfDemoThreads. + * Hence this pattern ensures dependent threads will start only after completion of demo threads . */ @Slf4j