170

In this code, what does the two joins and break mean? t1.join() causes t2 to stop until t1 terminates?

Thread t1 = new Thread(new EventThread("e1"));
t1.start();
Thread t2 = new Thread(new EventThread("e2"));
t2.start();
while (true) {
   try {
      t1.join();
      t2.join();
      break;
   } catch (InterruptedException e) {
      e.printStackTrace();
   }
}
Gray
  • 115,027
  • 24
  • 293
  • 354
user697911
  • 10,043
  • 25
  • 95
  • 169

11 Answers11

341

What does this thread join code mean?

To quote from the Thread.join() method javadocs:

join() Waits for this thread to die.

There is a thread that is running your example code which is probably the main thread.

  1. The main thread creates and starts the t1 and t2 threads. The two threads start running in parallel.
  2. The main thread calls t1.join() to wait for the t1 thread to finish.
  3. The t1 thread completes and the t1.join() method returns in the main thread. Note that t1 could already have finished before the join() call is made in which case the join() call will return immediately.
  4. The main thread calls t2.join() to wait for the t2 thread to finish.
  5. The t2 thread completes (or it might have completed before the t1 thread did) and the t2.join() method returns in the main thread.

It is important to understand that the t1 and t2 threads have been running in parallel but the main thread that started them needs to wait for them to finish before it can continue. That's a common pattern. Also, t1 and/or t2 could have finished before the main thread calls join() on them. If so then join() will not wait but will return immediately.

t1.join() means cause t2 to stop until t1 terminates?

No. The main thread that is calling t1.join() will stop running and wait for the t1 thread to finish. The t2 thread is running in parallel and is not affected by t1 or the t1.join() call at all.

In terms of the try/catch, the join() throws InterruptedException meaning that the main thread that is calling join() may itself be interrupted by another thread.

while (true) {

Having the joins in a while loop is a strange pattern. Typically you would do the first join and then the second join handling the InterruptedException appropriately in each case. No need to put them in a loop.

Gray
  • 115,027
  • 24
  • 293
  • 354
  • 28
    +1 That's a very strange pattern and probably can be removed. – m0skit0 Apr 11 '13 at 18:37
  • 4
    If t1 finish first, then t2 to finish. That seems to be a sequential process. One thread finishes first, then the other. what's the point of multiple threading? – user697911 Apr 11 '13 at 18:43
  • Probably we can totally remove the while loop? what does the loop do? Only two threads which are listed already. – user697911 Apr 11 '13 at 18:45
  • 10
    Because the `t1` and `t2` can run in parallel. It's just that the `main` needs them both to finish before it can continue. That's a typical pattern @user697911. – Gray Apr 11 '13 at 18:45
  • 3
    The `while` loop is there because (I guess) it wants to retry the `join()` calls if one is interrupted? I certainly would not write it that way @user697911. – Gray Apr 11 '13 at 18:46
  • But main is the entry point of a Java program. It always starts first. I am confused. You mean t1.join() and t2.join() to cause main to suspend temporarily on the half way and then continue the main thread? what could happen if I get rid of the two joins? – user697911 Apr 11 '13 at 18:51
  • Whatever thread that calls a `t1.join()` method will stop and wait until the `t1` thread finishes. If main is the thread that is making the call then yes, the main thread will wait for `t1` to finish. If you remove the `join()` calls then main will exit immediately and the `t1` and `t2` threads will continue running in the background. If `t1` and `t2` are daemon threads then if the main thread finishes the JVM will finish. – Gray Apr 11 '13 at 18:55
  • 5
    The loop is there to ensure that both `t1` and `t2` finish. Ie. if `t1` throws the `InterruptedException`, it will loop back and wait for `t2`. An alternative is to wait for both threads in each their Try-Catch, so the loop can be avoided. Also, depending on `EventThread`, it can make sense to do it this way, as we're running 2 threads, not one. – Michael Bisbjerg Jun 11 '13 at 17:05
  • 1
    THe `InterruptedException` is if the thread that is calling `join()` is interrupted, not either `t1` or `t2` @MichaelBisbjerg. If the calling thread is interrupted, it most likely should exit and not call `join()` again. But that depends on why it is interrupted. – Gray Nov 17 '13 at 14:03
  • @Gray, ah, I see. Explained here: http://stackoverflow.com/questions/10632451/does-calling-thread-interrupt-before-a-thread-join-cause-the-join-to-throw – Michael Bisbjerg Dec 15 '13 at 16:01
  • Want to emphasis on very important thing mentioned here :- "the main thread that is calling t1.join() will stop running and wait for the t1 thread to finish." Another important point is "The t2 thread is running in parallel and is not affected by t1." – Sankalp Mar 25 '16 at 11:01
  • Definition of join: "to bring in contact, connect, or bring or put together". Sounds about right to me @MelAdane. http://www.dictionary.com/browse/join?s=t – Gray Mar 06 '17 at 14:10
  • 2
    I still think `join` is not a good term. Better name? `waitUntilFinished()`. – Martin Marconcini Apr 27 '18 at 18:20
76

This is a favorite Java interview question.

Thread t1 = new Thread(new EventThread("e1"));
t1.start();
Thread e2 = new Thread(new EventThread("e2"));
t2.start();

while (true) {
    try {
        t1.join(); // 1
        t2.join(); // 2  These lines (1,2) are in in public static void main
        break;
    }
}

t1.join() means, t1 says something like "I want to finish first". Same is the case with t2. No matter who started t1 or t2 thread (in this case the main method), main will wait until t1 and t2 finish their task.

However, an important point to note down, t1 and t2 themselves can run in parallel irrespective of the join call sequence on t1 and t2. It is the main/daemon thread that has to wait.

UrsinusTheStrong
  • 1,239
  • 1
  • 16
  • 33
AmitG
  • 10,365
  • 5
  • 31
  • 52
  • 3
    Good example. About " can run in parallel": So what? It is important that the main thread will wait FIRST for t1 and THEN for t2. It really does not matter what t1 or t2 are doing (from the main thread perspective) – Alex Sep 20 '15 at 07:50
  • 1
    You mention the "main/daemon" thread. The main I understand but the daemon has nothing to do with it. The main thread is non-daemon. – Gray Feb 26 '16 at 16:42
  • 1
    `t1.join(); t2.join();` will not allow the thread that executes the joins to continue until both threads have terminated. In the absence of very unusual code elsewhere, the order of the joins does not matter. – David Schwartz Apr 19 '16 at 19:16
  • So will t2.join() be called only when t1 finished? – Leo DroidCoder Oct 04 '17 at 16:25
  • In other words, if we want to "serialize" execution of t1 and t2 threads, we need to place t1.join() right after t1.start() since main thread started t1 (and t2 afterwards) and of course remove it from try/catch. Obviously, doing so, the consequence will be a loss of parallelism. – dobrivoje May 25 '18 at 15:17
53

join() means waiting for a thread to complete. This is a blocker method. Your main thread (the one that does the join()) will wait on the t1.join() line until t1 finishes its work, and then will do the same for t2.join().

Avi
  • 21,182
  • 26
  • 82
  • 121
47

A picture is worth a thousand words.

    Main thread-->----->--->-->--block##########continue--->---->
                 \                 |               |
sub thread start()\                | join()        |
                   \               |               |
                    ---sub thread----->--->--->--finish    

Hope to useful, for more detail click here

xxy
  • 1,058
  • 8
  • 14
12

When thread tA call tB.join() its causes not only waits for tB to die or tA be interrupted itself but create happens-before relation between last statement in tB and next statement after tB.join() in tA thread.

All actions in a thread happen-before any other thread successfully returns from a join() on that thread.

It means program

class App {
    // shared, not synchronized variable = bad practice
    static int sharedVar = 0;
    public static void main(String[] args) throws Exception {
        Thread threadB = new Thread(() -> {sharedVar = 1;});
        threadB.start();
        threadB.join();

        while (true) 
            System.out.print(sharedVar);
    }
}

Always print

>> 1111111111111111111111111 ...

But program

class App {
    // shared, not synchronized variable = bad practice
    static int sharedVar = 0;
    public static void main(String[] args) throws Exception {
        Thread threadB = new Thread(() -> {sharedVar = 1;});
        threadB.start();
        // threadB.join();  COMMENT JOIN

        while (true)
            System.out.print(sharedVar);
    }
}

Can print not only

>> 0000000000 ... 000000111111111111111111111111 ...

But

>> 00000000000000000000000000000000000000000000 ... 

Always only '0'.

Because Java Memory Model don't require 'transfering' new value of 'sharedVar' from threadB to main thread without heppens-before relation (thread start, thread join, usage of 'synchonized' keyword, usage of AtomicXXX variables, etc).

Ivan Golovach
  • 199
  • 2
  • 5
6

Simply put:
t1.join() returns after t1 is completed.
It doesn't do anything to thread t1, except wait for it to finish.
Naturally, code following t1.join() will be executed only after t1.join() returns.

c0der
  • 18,467
  • 6
  • 33
  • 65
4

From oracle documentation page on Joins

The join method allows one thread to wait for the completion of another.

If t1 is a Thread object whose thread is currently executing,

t1.join() : causes the current thread to pause execution until t1's thread terminates.

If t2 is a Thread object whose thread is currently executing,

t2.join(); causes the current thread to pause execution until t2's thread terminates.

join API is low level API, which has been introduced in earlier versions of java. Lot of things have been changed over a period of time (especially with jdk 1.5 release) on concurrency front.

You can achieve the same with java.util.concurrent API. Some of the examples are

  1. Using invokeAll on ExecutorService
  2. Using CountDownLatch
  3. Using ForkJoinPool or newWorkStealingPool of Executors(since java 8)

Refer to related SE questions:

wait until all threads finish their work in java

Community
  • 1
  • 1
Ravindra babu
  • 37,698
  • 11
  • 250
  • 211
3

For me, Join() behavior was always confusing because I was trying to remember who will wait for whom. Don't try to remember it that way.

Underneath, it is pure wait() and notify() mechanism.

We all know that, when we call wait() on any object(t1), calling object(main) is sent to waiting room(Blocked state).

Here, main thread is calling join() which is wait() under the covers. So main thread will wait until it is notified. Notification is given by t1 when it finishes it's run(thread completion).

After receiving the notification, main comes out of waiting room and proceeds it's execution.

Arjun Patil
  • 101
  • 5
0

Hope it helps!

package join;

public class ThreadJoinApp {

    Thread th = new Thread("Thread 1") {
        public void run() {
            System.out.println("Current thread execution - " + Thread.currentThread().getName());
            for (int i = 0; i < 10; i++) {
                System.out.println("Current thread execution - " + Thread.currentThread().getName() + " at index - " + i);
            }
        }
    };

    Thread th2 = new Thread("Thread 2") {
        public void run() {
            System.out.println("Current thread execution - " + Thread.currentThread().getName());

            //Thread 2 waits until the thread 1 successfully completes.
            try {
            th.join();
            } catch( InterruptedException ex) {
                System.out.println("Exception has been caught");
            }

            for (int i = 0; i < 10; i++) {
                System.out.println("Current thread execution - " + Thread.currentThread().getName() + " at index - " + i);
            }
        }
    };

    public static void main(String[] args) {
        ThreadJoinApp threadJoinApp = new ThreadJoinApp();
        threadJoinApp.th.start();
        threadJoinApp.th2.start();
    }

    //Happy coding -- Parthasarathy S
}
Parthasarathy S
  • 197
  • 1
  • 6
0

The join() method is used to hold the execution of currently running thread until the specified thread is dead(finished execution).

Why we use join() method?

In normal circumstances we generally have more than one thread, thread scheduler schedules the threads, which does not guarantee the order of execution of threads.

Let's take a look at an example, create new project and copy the following code:

this is activity_main.xml code:

<?xml version="1.0" encoding="utf-8"?>

<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/btn_without_join"
    app:layout_constraintTop_toTopOf="parent"
    android:text="Start Threads Without Join"/>
 <Button
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:id="@+id/btn_with_join"
     app:layout_constraintTop_toBottomOf="@id/btn_without_join"
     android:text="Start Threads With Join"/>
 <TextView
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:id="@+id/tv"
     app:layout_constraintTop_toBottomOf="@id/btn_with_join"
     />

 </androidx.constraintlayout.widget.ConstraintLayout>

And this is code for MainActivity.java:

public class MainActivity extends AppCompatActivity {

TextView tv;
volatile  String threadName = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    tv = findViewById(R.id.tv);
    Button btn_without_join = findViewById(R.id.btn_without_join);
    btn_without_join.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            withoutJoin();
        }
    });
    Button btn_with_join = findViewById(R.id.btn_with_join);
    btn_with_join.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            withJoin();
        }
    });
}
private void withoutJoin()
{
    tv.setText("");
    Thread th1 = new Thread(new MyClass2(), "th1");
    Thread th2 = new Thread(new MyClass2(), "th2");
    Thread th3 = new Thread(new MyClass2(), "th3");
    th1.start();
    th2.start();
    th3.start();
}
private void withJoin()
{
    tv.setText("");
    Thread th1 = new Thread(new MyClass2(), "th1");
    Thread th2 = new Thread(new MyClass2(), "th2");
    Thread th3 = new Thread(new MyClass2(), "th3");
    th1.start();
    try {
        th1.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    th2.start();
    try {
        th2.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    th3.start();
}
class MyClass2 implements Runnable{

    @Override
    public void run() {
        Thread t = Thread.currentThread();
        runOnUiThread(new Runnable() {
            public void run() {
                tv.setText(tv.getText().toString()+"\n"+"Thread started: "+t.getName());
            }
        });
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        }
        runOnUiThread(new Runnable() {
            public void run() {
                tv.setText(tv.getText().toString()+"\n"+"Thread ended: "+t.getName());
            }
        });

    }
}
}

This will be the result if you press first button (Start Threads Without Join): enter image description here

And this will be the result if you press second button (Start Threads With Join): enter image description here

farhad.kargaran
  • 2,233
  • 1
  • 24
  • 30
-3

let's say our main thread starts the threads t1 and t2. Now, when t1.join() is called, the main thread suspends itself till thread t1 dies and then resumes itself. Similarly, when t2.join() executes, the main thread suspends itself again till the thread t2 dies and then resumes.

So, this is how it works.

Also, the while loop was not really needed here.

skmangalam
  • 359
  • 2
  • 8