Skip to content

Commit

Permalink
resolved checkstyle conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
keshavMM004 committed Jan 7, 2025
1 parent 3e23c5e commit 3d474c4
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 91 deletions.
184 changes: 100 additions & 84 deletions join/README.md
Original file line number Diff line number Diff line change
@@ -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/)
37 changes: 37 additions & 0 deletions join/etc/join.urm.puml
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions join/src/main/java/com/iluwatar/join/DemoThread.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
6 changes: 3 additions & 3 deletions join/src/main/java/com/iluwatar/join/DependentThread.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 ");
}

}
Expand Down
12 changes: 11 additions & 1 deletion join/src/main/java/com/iluwatar/join/JoinPattern.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
2 changes: 1 addition & 1 deletion join/src/main/java/com/iluwatar/join/JoinPatternDemo.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 ");

}

Expand Down

0 comments on commit 3d474c4

Please sign in to comment.