Learnitweb

Thread.join() method in Java with examples

  • join() method is provided by Thread class.
  • join() is a way of inter-thread synchronization.
  • This method lets one thread join at the end of another thread. This method allows one thread to wait until another thread completes its execution.
  • join() method has overloaded version which specifies the waiting period.
  • join() method creates a happen-before relationship.
  • The join() method may also return if the referenced thread was interrupted and will throw an InterruptedException.

There are three overloaded versions of join.

  • void join()
  • void join(long millis)
  • void join(long millis, int nanos)

When to use Thread.join() method

Suppose you have two threads, thread A and thread B. If the requirement is that thread B can not start and do its work until thread A has finished its work. In such case, you can join thread B at the end of thread A. If the referenced thread hasn’t been started or was already terminated, join method returns immediately.

Example of Thread.join() method

Let’s see the program without join() method and then with join() method.

Program without join() method

In the following code, we are creating two threads and starting these threads. There is no synchronization between the threads.

class MyRunnable implements Runnable {
	public void run() {
		System.out.println("Current thread executing: " + Thread.currentThread().getName());
		try {
            Thread.sleep(4000);
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        }
		System.out.println("Current thread executing: " + Thread.currentThread().getName() + " completed");
	}
}

public class ThreadExample {
	public static void main(String args[]) throws InterruptedException {
		System.out.println("Main thread started");
		Thread t1 = new Thread(new MyRunnable(), "thread 1");
		Thread t2 = new Thread(new MyRunnable(), "thread 2");
		t1.start();
		t2.start();
		System.out.println("Main thread completed");
	}
}

Output

Main thread started
Main thread completed
Current thread executing: thread 2
Current thread executing: thread 1
Current thread executing: thread 2 completed
Current thread executing: thread 1 completed

The output can be explained with following points:

  1. main method starts.
  2. thread 1 is created.
  3. thread 2 is created.
  4. thread 1 is started in a separate call stack.
  5. thread 2 is started in a separate call stack.
  6. main method completes.
  7. Out of two runnable threads, thread 1 and thread 2, thread scheduler chooses thread 2 and it begins execution.
  8. thread 1 begins execution.
  9. thread 2 completes.
  10. thread 1 completes.

As you can see main method does not wait for thread t1 and thread t2 to complete.

Let’s now see an example of using join() method.

The same program with join() method

We’ll write the above program with join() method.

class MyRunnable implements Runnable {
	public void run() {
		System.out.println("Current thread executing: " + Thread.currentThread().getName());
		try {
            Thread.sleep(4000);
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        }
		System.out.println("Current thread executing: " + Thread.currentThread().getName() + " completed");
	}
}

public class ThreadExample {
	public static void main(String args[]) throws InterruptedException {
		System.out.println("Main thread started");
		Thread t1 = new Thread(new MyRunnable(), "thread 1");
		Thread t2 = new Thread(new MyRunnable(), "thread 2");
		t1.start();
		t1.join();
		t2.start();
		t2.join();
		System.out.println("Main thread completed");
	}
}

Output

Main thread started
Current thread executing: thread 1
Current thread executing: thread 1 completed
Current thread executing: thread 2
Current thread executing: thread 2 completed
Main thread completed

The output can be explained with the help of following points:

  1. main method starts.
  2. Thread t1 is created.
  3. Thread t2 is created.
  4. Thread t1 is started.
  5. The current thread which is the main method joins thread t1, that is, joins the thread referenced by t1 onto the end. main method will not become runnable until t1 is executed.
  6. Thread t2 is started.
  7. The current thread which is the main method joins thread t2, that is, joins the thread referenced by t2 onto the end. main method will not become runnable until t2 is executed.
  8. main method completes.

join() method with specified wait time

The current thread in our program will keep waiting if no wait time is specified. This could be a problem as the calling thread could wait if the referenced thread on which join() method is called becomes non-responsive. The overloaded version of join allows to specify a wait time.

A timeout of 0 means to wait forever.

In the following example, we have specified 2000 milliseconds as the wait time.

class MyRunnable implements Runnable {
	public void run() {
		System.out.println("Current thread executing: " + Thread.currentThread().getName());
		try {
            Thread.sleep(4000);
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        }
		System.out.println("Current thread executing: " + Thread.currentThread().getName() + " completed");
	}
}

public class ThreadExample {
	public static void main(String args[]) throws InterruptedException {
		System.out.println("Main thread started");
		Thread t1 = new Thread(new MyRunnable(), "thread 1");
		Thread t2 = new Thread(new MyRunnable(), "thread 2");
		t1.start();
		t1.join(2000);
		t2.start();
		t2.join(2000);
		System.out.println("Main thread completed");
	}
}

Output

Main thread started
Current thread executing: thread 1
Current thread executing: thread 2
Main thread completed
Current thread executing: thread 1 completed
Current thread executing: thread 2 completed

In this program, wait time is only of 2 seconds as defined in join argument whereas the sleep time of thread 1 and thread 2 is 4 seconds. So main method will get a chance to execute before thread 1 could complete.

In short following will be the sequence with respect to main method and thread 1:

  1. thread 1 starts.
  2. main method waits for 2 seconds before getting a chance to execute.
  3. thread 1 sleeps for 4 seconds before completing execution.
  4. main method gets a chance to execute after 2 seconds wait time is over.

join() method and Synchronization

join() method can be used to create a happen-before relationship. When a thread t1 calls t2.join() then t1 will only execute once t2 has executed. This creates a happen-before relationship between thread t1 and t2.