Guide to Join Threads in Java

In this tutorial, we will learn how to join two Threads and why there is a need to join Threads in java. We will explore in detail the Thread.join() API and the different versions of the same along with practical examples.

We will also see how we can end up in a deadlock situation while using join() and the ways to use it effectively in our code.

1. Introduction to Thread Joining

Thread is a lightweight process that allows a program to operate more efficiently by running multiple threads in parallel. If we have a requirement where the first Thread must wait until the second Thread completes its execution then we should join these 2 Threads.

Let’s take an example to understand it more clearly. Suppose we have 3 Threads performing 3 different kinds of activities for a marriage application.

  • The wedding cards distribution activity starts once the wedding cards printing activity completes and the wedding cards printing activity starts once we fix the venue for the wedding.
  • We can say that in this case, Thread-3 has to wait until Thread-2 completes and also Thread-2 has to wait until Thread-1 completes its execution.
  • Thread-2 has to call Thread-1.join(), and Thread-3 has to call Thread-2.join() so that they will wait until the completion of the other thread.

2. Java Thread.join() API

The join() method makes a calling Thread enters into waiting for the state until the Thread on which join() is called completes its execution.

  • A Thread 't1' wants to wait until another Thread 't2' completes its execution then t1 has to call the join() method on t2,
  • t2.join() called by t1.
  • When t1 executes t2.join() then t1 enters into waiting state immediately and continues to wait until t2 completes its execution and terminates.
  • Once t2 completes then only t1 continues its execution again.
thread join
Life Cycle of a Joined Thread

2.1 Joining without Timeout

The default join() method makes the current Thread wait indefinitely until the Thread on which it is called is completed. If the Thread is interrupted then it will throw InterruptedException.

public final void join() throws InterruptedException

Let’s now look at an example of how to use join() method.

public class ChildThread extends Thread
{  
         public void run()
         {  
              for(int i=1; i<=4; i++)
              {  
                 try{  
                         Thread.sleep(500);  
                      } catch(Exception e){
                          System.out.println(e);
                      }
                 System.out.println(“child thread execution - ” + i);  
              }  
         }  
}
ChildThread th1 = new ChildThread();

// Starting child Thread
th1.start();

// main thread joining the child thread
th1.join();

// main Thread printing statements
System.out.println(“main thread completed”);

Notice the program output. The main thread is calling join() method on child thread and waiting for its completion so after calling join(). So, all the statements from the child thread print before the main thread statements.

child thread execution - 1
child thread execution - 2
child thread execution - 3
child thread execution – 4
main thread completed

2.2 Joining with Timeout

  • join(long millis): It makes the current Thread wait until the Thread on which it is called is completed or the time specified in milliseconds has expired.
  • join(long millis, int nanos): It makes the current Thread wait until the Thread on which it is called is completed or the time specified in milliseconds + nanoseconds has expired.
public final synchronized void join(long millis) throws InterruptedException

public final synchronized void join(long millis, int nanos) throws InterruptedException

Let’s now look at how it works with an example.

// Creating child Thread Object
ChildThread th1 = new ChildThread();

// Starting child Thread
th1.start();

//main thread joining the child thread with 1000ms expiration time
th1.join(1000);

// main Thread printing statements
System.out.println(“main thread completed”);

When the program executes, the main thread will wait at max 1000ms and will be invoked anytime after that.

child thread execution - 1
main thread completed
child thread execution - 2
child thread execution - 3
child thread execution - 4

3. Interrupting a Joined Thread

A waiting Thread can be interrupted by another Thread using the interrupt() method. Once the waiting Thread is interrupted, it immediately comes out of the waiting state and moves into Ready/Runnable state and waits for the Thread Scheduler to allocate the processor to begin the execution again.

public class ThreadJoiningInterrupt {
  
  public static void main(String[] args) {

    ChildThread childThread = new ChildThread(Thread.currentThread());
    childThread.start();

    try {

      childThread.join();   //Joined

    } catch (InterruptedException ie) {
      System.out.println("main Thread is interrupted");
    }

    for (int i = 1; i <= 4; i++) {
      System.out.println("main Thread Execution - " + i);
    }
  }
}

class ChildThread extends Thread {

  private static Thread parentThreadRef;

  public ChildThread(Thread parentThreadRef) {
    this.parentThreadRef = parentThreadRef;
  }

  public void run() {

    parentThreadRef.interrupt();   //Interrupted

    for (int i = 1; i <= 4; i++) {
      System.out.println("Child Thread Execution - " + i);
    }
  }
}

Notice the program output, once the child thread interrupts the joined main thread then both thread executes simultaneously.

Child Thread Execution - 1
main Thread is interrupted
Child Thread Execution - 2
main Thread Execution - 1
main Thread Execution - 2
main Thread Execution - 3
main Thread Execution - 4
Child Thread Execution - 3
Child Thread Execution – 4

4. Watch out for Deadlocks

We must be careful while using join() method as sometimes it may lead to a deadlock situation. If two Threads call the join() method on each other then both threads enter into waiting for state and wait for each other forever.

Similarly, if a thread, by mistake, joins itself then it will be blocked forever, and JVM must be shutdown to recover.

class Threadjoindemo
{
public static void main(String[] args)
{
    // main thread calling join() on itself
    Thread.currentThread().join();                     // Program gets stuck, Deadlock suitation
}
}

5. Conclusion

In this tutorial, we have learned the need to join two threads and how to use Thread.join() API with or without the expiration duration. We have also seen how we can interrupt a joined Thread. We also learned avoiding deadlock situations while using join().

Happy Learning!!

Comments

Subscribe
Notify of
guest
2 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.