Guide to Joining 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”);

Whenthe 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 the another Thread using the interrupt() method. Once the waiting Thread is interruptted, 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.

// Creating child Thread by extending Thread class
public class ThreadjoiningInterrupt extends Thread 
{
     // main Thread reference
	private static Thread mt;

    // Job of child Thread
	public void run() 
        {
		for (int i = 1; i <= 4; i++) {

                    // child Thread interrupting main Thread
			mt.interrupt();

                    // child Thread prints statements
			System.out.println("Child Thread Execution - " + i);
		}
	}

    // main Thread Job
	public static void main(String[] args) 
        {
            // Setting main Thread object in 'mt' reference
		Demo.mt = Thread.currentThread();

            // Instantiating child Thread
		Demo t1 = new Demo();

            // Starting child Thread
		t1.start();

		try {
                    // main Thread calls join() on child Thread
			t1.join();
		} catch (InterruptedException ie) {
                     // catching Interrupted Exception
			System.out.println("main Thread is interrupted");
		}

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

Notice the program output, once the child thread interrupts the 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 have to 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 the threads enter into waiting for state and wait for each other forever.

Similarly, if a thread, by mistae, join to itself then it will be blocked forever and JVM mut 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 of joining 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 avoding deadlock situations while using join().

Happy Learning!!

Leave a Reply

0 Comments
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.