join()
method is provided byThread
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 anInterruptedException
.
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:
main
method starts.- thread 1 is created.
- thread 2 is created.
- thread 1 is started in a separate call stack.
- thread 2 is started in a separate call stack.
main
method completes.- Out of two runnable threads, thread 1 and thread 2, thread scheduler chooses thread 2 and it begins execution.
- thread 1 begins execution.
- thread 2 completes.
- 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:
main
method starts.- Thread t1 is created.
- Thread t2 is created.
- Thread t1 is started.
- 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. - Thread t2 is started.
- 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. 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:
- thread 1 starts.
main
method waits for 2 seconds before getting a chance to execute.- thread 1 sleeps for 4 seconds before completing execution.
- 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.