Java wait()/join(): Why does this not deadlock? Java wait()/join(): Why does this not deadlock? multithreading multithreading

Java wait()/join(): Why does this not deadlock?


join() method internally calls wait() which will result in releasing of the lock(of Thread object).

See the code of join() below:

public final synchronized void join(long millis)     throws InterruptedException {    ....    if (millis == 0) {       while (isAlive()) {         wait(0);  //ends up releasing lock       }    }    ....}

Reason why your code sees this and not seen in general:: The reason why your code see this and not is not observed in general, is because the join() method waits() on Thread object itself and consequently relinquishes lock on the Thread object itself and as your run() method also synchronizes on the same Thread object, you see this otherwise unexpected scenario.


The implementation of Thread.join uses wait, which lets go of its lock, which is why it doesn't prevent the other thread from acquiring the lock.

Here is a step-by-step description of what happens in this example:

Starting the MyThread thread in the main method results in a new thread executing the MyThread run method. The main Thread sleeps for a whole second, giving the new Thread plenty of time to start up and acquire the lock on the MyThread object.

The new thread can then enter the wait method and release its lock. At this point the new thread goes dormant, it won't try to acquire the lock again until it is woken up. The thread does not return from the wait method yet.

At this point the main thread wakes up from sleeping and calls shutdown on the MyThread object. It has no problem acquiring the lock because the new thread released it once it started waiting. The main thread calls notify now. Entering the join method, the main thread checks that the new thread is still alive, then waits, releasing the lock.

The notification happens once the main thread releases the lock. Since the new thread was in the wait set for the lock at the time the main thread called notify, the new thread receives the notification and wakes up. It can acquire the lock, leave the wait method, and finish executing the run method, finally releasing the lock.

The termination of the new thread causes all threads waiting on its lock to receive a notification. This wakes up the main thread, it can acquire the lock and check that the new thread is dead, then it will exit the join method and finish executing.

/** * Waits at most <code>millis</code> milliseconds for this thread to  * die. A timeout of <code>0</code> means to wait forever.  * * @param      millis   the time to wait in milliseconds. * @exception  InterruptedException if any thread has interrupted *             the current thread.  The <i>interrupted status</i> of the *             current thread is cleared when this exception is thrown. */public final synchronized void join(long millis) throws InterruptedException {long base = System.currentTimeMillis();long now = 0;if (millis < 0) {        throw new IllegalArgumentException("timeout value is negative");}if (millis == 0) {    while (isAlive()) {    wait(0);    }} else {    while (isAlive()) {    long delay = millis - now;    if (delay <= 0) {        break;    }    wait(delay);    now = System.currentTimeMillis() - base;    }}}


To complement the other answers: I see no mention of join() releasing any locks in the API-documentation, so this behavior is actually implementation-specific.

Learn from this:

  • don't subclass Thread, instead use a Runnable implementation passed to your thread object.
  • don't synchronize/wait/notify on objects you don't "own", e.g. where you don't know who else might synchronize/wait/notify on it.