Java Thread Join Example
In this article, I am going to show you an example of Thread.join
. It can be used in a scenario where the calling thread has to rely on the results of execution of one or more sub-tasks and can’t proceed unless the threads executing these sub-tasks are done with their job.
In brief, Thread.join()
provides you with wait & notification mechanism where the execution of the calling thread is suspended until the object called finishes its execution.
Before we start with the examples, let’s first go through the mechanics of Thread.join
.
1. Thread.Join Flow
When Thread.join
is called, it suspends the execution of the calling thread until the target thread is alive. Once the target thread is done with its job and terminates, the caller thread will automatically wake up.
In the below diagram, the caller thread starts two threads t1
and t2
, each thread is assigned one specific task. The caller thread then calls on t1.join
so that it is blocked from further execution till t1
finishes its job. Once t1
is done with its execution and terminates, it wakes up the caller thread. Next, caller thread calls t2.join
to wait on t2
till it is done with its task. Once t2
finishes, caller thread wakes up and continues in its execution.
Thread.join
throws InterruptedException
, if any thread has interrupted the current thread so when we call it we must make sure it is caught and dealt appropriately.
2. Thread.join variations
There are couple of variations on join()
method:
join (long milliseconds)
join (long milliseconds, long nanos)
In the first version of the join()
method, instead of waiting indefinitely for the thread called to terminate, the calling thread waits for the milliseconds specified as a parameter of the method.
The second version of the join()
method is very similar to the first version, it receives the waiting period in number of milliseconds and the number of nanoseconds.
3. Example of Thread.join()
In this example, the main thread creates two threads and assigns each one a task. It then calls on join
to wait for the thread’s completion. The first task calculates the average of numbers and the second task calculates the median. Once both tasks are done, the main thread prints the results.
ThreadJoinExample:
package com.javacodegeeks.threads; import java.util.Arrays; import java.util.Collections; import java.util.List; public class ThreadJoinExample { public static void main(String[] args) { Integer[] values = new Integer[] { 3, 1, 14, 3, 4, 5, 6, 7, 8, 9, 11, 3, 2, 1 }; Average avg = new Average(values); Median median = new Median(values); Thread t1 = new Thread(avg, "t1"); Thread t2 = new Thread(median, "t2"); System.out.println("Start the thread t1 to calculate average"); t1.start(); System.out.println("Start the thread t2 to calculate median"); t2.start(); try { System.out.println("Join on t1"); t1.join(); System.out .println("t1 has done with its job of calculating average"); } catch (InterruptedException e) { System.out.println(t1.getName() + " interrupted"); } try { System.out.println("Join on t2"); t2.join(); System.out .println("t2 has done with its job of calculating median"); } catch (InterruptedException e) { System.out.println(t2.getName() + " interrupted"); } System.out.println("Average: " + avg.getMean() + ", Median: " + median.getMedian()); } /** * Calculate average of numbers. Sum all the int values and divide it by * total count. */ private static class Average implements Runnable { private Integer[] values; private int mean; Average(Integer[] values) { this.values = values; } @Override public void run() { mean = 0; int n = values.length; for (int i : values) { mean += i; } mean /= n; } public int getMean() { return mean; } } /** * Sorts the given int list and calculates the median value. If size is * even, the mean of middle and middle-1. * */ private static class Median implements Runnable { private Integer[] values; private int median; Median(Integer[] values) { this.values = values; } @Override public void run() { List sortedList = Arrays.asList(values); Collections.sort(sortedList); int n = values.length; int middle = n / 2; if (n % 2 == 0) { median = (sortedList.get(middle) + sortedList.get(middle - 1)) / 2; } else { median = sortedList.get(middle); } } public int getMedian() { return median; } } }
Output:
Start the thread t1 to calculate average Start the thread t2 to calculate median Join on t1 t1 has done with its job of calculating average Join on t2 t2 has done with its job of calculating median Average: 5, Median: 4
4. Example of Thread.join(millis)
In this example, I will show the other variation of Thread.join
which takes the waiting period as the parameter. The calling thread will wait only for the number of milliseconds passed in.
The main thread creates a thread and assigns it a sub task. Just to make sure sub task takes a while to complete, I have called Thread.sleep(100000)
so that it is blocked for 100000 millis. Once the thread is started, the main thread calls on t.join(100)
so that it can wake up after 100 millis irrespective of whether the called thread has done its job or not. After waking up, it checks whether the called thread is still alive. If it is, then it simply interrupts the thread to end its execution.
ThreadJoinMillisExample:
package com.javacodegeeks.threads; public class ThreadJoinMillisExample { public static void main(String[] args) { SubTask subTask = new SubTask(); Thread t = new Thread(subTask); t.start(); try { System.out .println("Join on subTask thread but only for 100 millis"); t.join(100); System.out.println("Main thread came out of join"); } catch (InterruptedException e) { System.out.println("Main thread Interrupted!"); } if (t.isAlive()) { System.out.println("SubTask is still alive, interrupt it"); t.interrupt(); } } private static class SubTask implements Runnable { @Override public void run() { try { Thread.sleep(100000); } catch (InterruptedException e) { System.out.println("Subtask Interrupted!"); } } } }
Output:
Join on subTask thread but only for 100 millis Main thread came out of join SubTask is still alive, interrupt it Subtask Interrupted!
5. Example of InterruptedException
In this example, I will show you how Thread.join
can be interrupted by the thread on which the calling thread is waiting. The main thread creates a thread and assigns a sub task to it and then calls on t.join()
to wait for the results. The sub task thread interrupts the main thread once it has the results the main thread is interested in. After interruption, the sub task continues further executing some more task. Meanwhile, the main thread wakes up due to interruption and obtains the result from sub task.
ThreadJoinInterruptionExample:
package com.javacodegeeks.threads; public class ThreadJoinInterruptionExample { public static void main(String[] args) { ThreadJoinInterruptionExample caller = new ThreadJoinInterruptionExample(); caller.startSubTask(); } public void startSubTask() { System.out.println("Start subtask"); SubTask subTask = new SubTask(Thread.currentThread()); Thread t = new Thread(subTask); t.start(); try { t.join(); } catch (InterruptedException e) { System.out.println("Interrupted, lets check the result"); } System.out.println("Got the result? " + (subTask.getResult() != null)); } private class SubTask implements Runnable { private Thread callerThread; private Object result; SubTask(Thread callerThread) { this.callerThread = callerThread; } @Override public void run() { doTask(); System.out .println("task done, interrupt the thread waiting on the result"); callerThread.interrupt(); doSomeMoreTask(); } private void doTask() { System.out.println("do task"); result = new Object(); } private void doSomeMoreTask() { System.out.println("do some more task"); } public Object getResult() { return result; } } }
Output:
Start subtask do task task done, interrupt the thread waiting on the result do some more task Interrupted, lets check the result Got the result? true
Download the Eclipse Project
In this article, I have shown you Java Thread Join Examples.
You can download the source code here: threadjoin.zip