Java Thread Join Example
In Java, threads are a fundamental part of concurrent programming, allowing multiple tasks to run in parallel. However, managing the execution order of threads and ensuring synchronization is crucial to avoid unexpected behaviors. The join method is one such mechanism that helps in achieving synchronization between threads.
In this article, we’ll explore the join method in Java threads, understand its usage, and see practical examples to demonstrate its functionality.
Understanding the Join Method
The join method in Java is used to wait for a thread to complete its execution before proceeding to the next steps in the program. When a thread invokes the join method on another thread, it essentially waits for that thread to finish. This is particularly useful when the outcome of one thread is dependent on the result of another.
Basic Syntax
public final void join() throws InterruptedException
The join method can throw an InterruptedException, so handling this exception is essential.
Method 1: Basic Join
Let’s start with a simple example. Consider two threads: ThreadA and ThreadB. We want ThreadB to wait for ThreadA to finish before proceeding.
class ThreadA extends Thread {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Thread A - Count: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class ThreadB extends Thread {
private Thread threadA;
public ThreadB(Thread threadA) {
this.threadA = threadA;
}
public void run() {
try {
System.out.println("Thread B waiting for Thread A to finish.");
threadA.join();
System.out.println("Thread B continuing after Thread A finishes.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class JoinExample {
public static void main(String[] args) {
ThreadA threadA = new ThreadA();
ThreadB threadB = new ThreadB(threadA);
threadA.start();
threadB.start();
}
}
Output :
Thread A - Count: 1
Thread A - Count: 2
Thread A - Count: 3
Thread A - Count: 4
Thread A - Count: 5
Thread B waiting for Thread A to finish.
Thread A - Count: 6
Thread A - Count: 7
Thread A - Count: 8
Thread A - Count: 9
Thread A - Count: 10
Thread B continuing after Thread A finishes.
In this example, ThreadB uses the join method to wait for ThreadA to complete its execution.
Method 2: Join with Timeout
The join method also has an overloaded version that takes a timeout. This allows the waiting thread to proceed after a specified time, even if the target thread hasn’t finished.
public final void join(long millis) throws InterruptedException
Let’s modify our previous example to include a timeout.
// ... (previous code)
Â
class ThreadB extends Thread {
private Thread threadA;
Â
public ThreadB(Thread threadA) {
this.threadA = threadA;
}
Â
public void run() {
try {
System.out.println("Thread B waiting for Thread A for 2 seconds.");
threadA.join(2000);
System.out.println("Thread B continuing after waiting for 2 seconds.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Â
// ... (main method)
Output:
Thread A - Count: 1
Thread A - Count: 2
Thread A - Count: 3
Thread A - Count: 4
Thread A - Count: 5
Thread B waiting for Thread A for 2 seconds.
Thread A - Count: 6
Thread A - Count: 7
Thread A - Count: 8
Thread A - Count: 9
Thread A - Count: 10
Thread B continuing after waiting for 2 seconds.
Method 3: Join with Order
Consider a scenario where three threads (ThreadA, ThreadB, and ThreadC) need to execute in a specific order. We can use the join method to achieve this.
// ... (previous code)
class ThreadC extends Thread {
private Thread threadB;
public ThreadC(Thread threadB) {
this.threadB = threadB;
}
public void run() {
try {
System.out.println("Thread C waiting for Thread B to finish.");
threadB.join();
System.out.println("Thread C continuing after Thread B finishes.");
for (int i = 6; i <= 10; i++) {
System.out.println("Thread C - Count: " + i);
Thread.sleep(500);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// ... (main method)
Output:
Thread A - Count: 1
Thread A - Count: 2
Thread A - Count: 3
Thread A - Count: 4
Thread A - Count: 5
Thread B waiting for Thread A to finish.
Thread A - Count: 6
Thread A - Count: 7
Thread A - Count: 8
Thread A - Count: 9
Thread A - Count: 10
Thread B continuing after Thread A finishes.
Thread C waiting for Thread B to finish.
Thread C continuing after Thread B finishes.
Thread C - Count: 6
Thread C - Count: 7
Thread C - Count: 8
Thread C - Count: 9
Thread C - Count: 10
In this example, ThreadC waits for ThreadB to finish, ensuring that the execution order is maintained.
Conclusion
The join method in Java threads provides a powerful mechanism for managing thread synchronization. It allows threads to wait for each other, ensuring a specific execution order and coordination. Understanding and effectively using the join method is essential for building robust multithreaded applications.
In summary, we covered the basic syntax of the join method, demonstrated its usage with simple examples, and explored scenarios with timeouts and ordered execution. Incorporating the join method into your threading toolkit can significantly enhance the efficiency and reliability of your concurrent Java programs.