How to join two threads in Java? Thread.join() example

You can join two threads in Java by using the join() method from java.lang.Thread class. Why do you join threads? because you want one thread to wait for another before starts processing. It's like a relay race where the second runner waits until the first runner comes and hands over the flag to him. Remember, unlike sleep(), join() is not a static method so you need an object of java.lang.Thread class to call this method. Now, who calls and who wants, and which thread dies? for example, if you have two threads in your program main thread and T1 which you have created. 

Now if your main thread executes this line T1.join() (main thread execute whatever you have written in the main method) then the main thread will wait for the T1 thread to finish its execution. 

The immediate effect of this call would be that the main thread will stop processing immediately and will not start until the T1 thread has finished. 

So, what will happen if T1 is not yet started and there is no one to start T1 thread? Well,  then you have a deadlock but if it's already finished then the main thread will start again, provided it get the CPU back.

In this tutorial, you will learn how to make three threads execute in order by using the join() method. You can also check out Java Threads By Scott Oaks and Henry Wong to get a good starting overview of several fundamentals of threads and multi-threading.



Thread.join() Example in Java

In this program, I have tried to explain how to use the join() method by solving a popular multi-threading interview question, how to execute three threads in order, such that T1 will start first and finished last. In this example, I have created a class called ParallelTask which implements a Runnable interface and used to create multiple threads.

In the run() method it first prints a message that it has started and then just calls the join() method to wait for its predecessor thread to die before starting again, after that it prints the finished execution. I have made these arrangements so that all thread finish their execution in order T3, T2, and T1.

AfterI creates an object of Thread T1, I set its predecessor as T2. Similarly, I set T3 as the predecessor of T2. Since it's not guaranteed that when a thread will start after calling the start() method, you may see that T3 starts before T2, even if we have called T2.start() prior to T3.start(), but by putting those join() statements, we have guaranteed their execution. There is no way that T2 will finish before T3 and T1 will finish before T2.

You can also check out Core Java Volume 1 - Fundamentals by Cay S. Horstmann to get a good starting overview of several fundamentals of threads and multi-threading.

How to join two thread in Java


Now, let me explain you execution of this program. Our program starts by JVM invoking a main method() and the thread which executes the main method is called the main thread. How do you I know that? do you remember those "Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException" exceptions? You can see that Java prints the name of the thread as "main".

In our main() method we have first created three objects of ParallelTask class which is nothing but Runnable. These tasks will be executed by three threads we will create next. We have given all threads meaningful names to understand output better, it's actually best practice to name your threads.

Next, I have set a predecessor for each thread, it's nothing but another thread object and our ParallelTask has a variable predecessor to hold reference of thread to which this thread will join. This is critical to ensure ordering between multiple threads.

Since each thread calls predecessor.join() method, by setting right predecessors you can impose order. In our example, threads will start in any order but will do processing such that T3 will finish first, followed by T2, and last by T1.


import java.util.concurrent.TimeUnit;

/**
 * Simple Java Program to show how to execute threads in a particular order. You
 * can enforce ordering or execution sequence using Thread.join() method in
 * Java.
 *
 * @author Javin Paul
 */
public class JoinDemo {

    private static class ParallelTask implements Runnable {
        private Thread predecessor;

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " Started");

            if (predecessor != null) {
               
                try {
                    predecessor.join();
                   
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.println(Thread.currentThread().getName() + " Finished");
        }

        public void setPredecessor(Thread t) {
            this.predecessor = t;
        }

    }

    public static void main(String[] args) {

        // we have three threads and we need to run in the
        // order T1, T2 and T3 i.e. T1 should start first
        // and T3 should start last.
        // You can enforce this ordering using join() method
        // but join method must be called from run() method
        // because the thread which will execute run() method
        // will wait for thread on which join is called.

        ParallelTask task1 = new ParallelTask();
        ParallelTask task2 = new ParallelTask();
        ParallelTask task3 = new ParallelTask();

        final Thread t1 = new Thread(task1, "Thread - 1");
        final Thread t2 = new Thread(task2, "Thread - 2");
        final Thread t3 = new Thread(task3, "Thread - 3");

        task2.setPredecessor(t1);
        task3.setPredecessor(t2);

        // now, let's start all three threads
        t1.start();
        t2.start();
        t3.start();
    }

}

Output
Thread - 1 Started
Thread - 1 Finished
Thread - 2 Started
Thread - 3 Started
Thread - 2 Finished
Thread - 3 Finished

From the output, you can see that threads have started in a different order than the order their start() method has called, this is fine because you don't know which thread will get CPU. It all depends upon the mood of the thread scheduler, but you can see that threads are finished in the correct order. Thread 3 finished first, Thread 2 second, and Thread 1 last, the order we wanted. This is achieved by using the join() method of java.lang.Thread class. It's a very useful method to learn.

Some important points about the join method in Java :

Thread.join() example in Java

That's all about how to join two threads in Java using the Thread.join() method. You can use this to impose ordering between multiple threads like suppose you want to start processing when all your caches are loaded. Though there are some concurrency utilities like CylicBarrier and CountDownLatch exists, knowing how it works at the join level is very good for any Java developer. Let me know if you have doubts or questions about the Thread.join() method in Java.

Further Learning
Multithreading and Parallel Computing in Java
Applying Concurrency and Multi-threading to Common Java Patterns
Java Concurrency in Practice - The Book
Java Concurrency in Practice Bundle by Heinz Kabutz

Other Java Multithreading tutorials for curious developers :
  • What is the difference between the transient and volatile variables in Java? [answer]
  • How to stop a thread in Java? [solution]
  • How to pause a thread in Java? [example]
  • What is the difference between a thread and a process? [answer]
  • How to use CyclicBarrier class in Java? [example]
  • What is the difference between implements Runnable and extends Thread in Java? [answer]
  • 10 things you should know about Thread in Java? [article]
  • Difference between CountDownLatch and CyclicBarrier in Java? [answer]
  • When to use the wait() and sleep() method in Java? [answer]
  • How to use CountDownLatch in Java? [example]
  • Difference between a Callable and a Runnable interface in Java? [answer]

13 comments:

  1. // we have three threads and we need to run in the
    // order T1, T2 and T3 i.e. T1 should start first
    // and T3 should start last.
    //The code should be like this then
    task2.setPredecessor(t1);
    task3.setPredecessor(t2);

    //Then the output will look like below. See how Thread 1 started and then Thread 2 and then Thread 3.
    Thread-1 started
    Thread-2 started
    Thread-1 finished
    Thread-3 started
    Thread-2 finished
    Thread-3 finished

    ReplyDelete
    Replies
    1. @Anonymous, good spot :) that was typo which is well caught by you. If you notice output was also showing that clearly, corrected now.

      Delete
  2. Javin, what you said & what your program output shows is contradictory. You said, T3 should finish 1st, T2 should finish 2nd, T1 should finish last. Pls see the program output. The finishing order is exactly reverse of what you want.

    ReplyDelete
    Replies
    1. Rightly said Ajinkya. It depends on the predecessor set in the program to maintain the ordering.

      Delete
  3. import java.util.concurrent.TimeUnit;
    /** * Simple Java Program to show how to execute threads in a particular order. You can enfo
    rce ordering or execution sequence using Thread.join() method in * Java. * * @author Javin Paul */

    public class JoinDemo {
    private static class ParallelTask implements Runnable {
    private Thread SubProcess;
    @Override
    public void run() {
    System.out.println(Thread.currentThread().getName() + " Started");
    if ( SubProcess!= null) {
    try { SubProcess.join(); }
    catch (InterruptedException e) { e.printStackTrace(); }
    }
    System.out.println(Thread.currentThread().getName() + " Finished");
    }

    public void setSubProcess(Thread t) {
    this.SubProcess = t;
    }

    }
    public static void main(String[] args) {
    // we have three threads and we need to run in the
    // order T1, T2 and T3 i.e. T1 should start first
    // and T3 should start last.
    // You can enforce this ordering using join() method
    // but join method must be called from run() method
    // because the thread which will execute run() method
    // will wait for thread on which join is called.
    ParallelTask task1 = new ParallelTask();
    ParallelTask task2 = new ParallelTask();
    ParallelTask task3 = new ParallelTask();

    final Thread t1 = new Thread(task1, "Thread - 1");
    final Thread t2 = new Thread(task2, "Thread - 2");
    final Thread t3 = new Thread(task3, "Thread - 3");

    task1.setSubProcess(t2);
    task2.setSubProcess(t3);
    // now, let's start all three threads
    t1.start();
    t2.start();
    t3.start();

    }
    }

    ReplyDelete
  4. OUTPUT:

    Thread - 1 Started
    Thread - 1 Finished
    Thread - 2 Started
    Thread - 2 Finished
    Thread - 3 Started
    Thread - 3 Finished

    ReplyDelete
  5. I just tested and it gives the below output:

    Thread - 1 Started
    Thread - 1 Finished
    Thread - 2 Started
    Thread - 2 Finished
    Thread - 3 Started
    Thread - 3 Finished

    ReplyDelete
  6. Hi I am running this code today and every time i am getting unpredictable output

    ReplyDelete
    Replies
    1. Hello @Unknown, what output you get? why do you think its unpredictible?

      Delete
  7. Setting t1 to depend on t2 to finish and t2 to depend on t3 to finish like this -

    task1.setPredecessor(t2);
    task2.setPredecessor(t3);

    Ensures order of finishing is t3,t2 and then t1.

    Output:
    Thread - 3 Started
    Thread - 1 Started
    Thread - 2 Started
    Thread - 3 Finished
    Thread - 2 Finished
    Thread - 1 Finished

    ReplyDelete
    Replies
    1. where do you get the setPredecessor method, never heard about it? which class it belogs to?

      Delete

Feel free to comment, ask questions if you have any doubt.