Here is a basic code example

Main Main thread

T1 is the child thread

public static void main(String[] args) throws InterruptedException {
    // Initialization state
    Thread t1 = new Thread(() -> {}); 
​
    // Runnable/running state, as for when running is controlled by the underlying platform
    t1.start(); 
​
    // Block, waiting for the T1 thread to finish executing
    t1.join(); 
}
Copy the code

Thread.join() source code analysis

Join (), join(long millis), join(long millis, int nanos)

public final void join(a) throws InterruptedException {}
public final synchronized void join(long millis) throws InterruptedException {}
public final synchronized void join(long millis, int nanos) throws InterruptedException {}
Copy the code

The join() method is equivalent to join(0)

Join (long millis, int nanos) also calls join(long millis)

So let’s use join(Long millis) as an example

/** * wait at most millis milliseconds for the thread to terminate. A timeout of 0 means waiting forever. * This method calls this.watit using a loop to determine isAlive(). When the future terminates, the this.notifyAll method is called. * Applications are advised not to use Wait, notify, or notifyAll * * on Thread instances@paramMillis - Wait time in milliseconds *@throwsIllegalArgumentException - If the value of millis is negative *@throwsInterruptedException - If any thread interrupts the current thread. Clears the interrupted state of the current thread 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; }}}Copy the code

And the core of that is

// While (isAlive()) prevents the child thread from spurious wakeup,
// As long as the child thread (T1) does not terminate, the parent thread (main) must continue to wait.
while (isAlive()) {
     wait(0);
 }
Copy the code

IsAlive () is a final native method

/** * Tests whether the thread is alive. If a thread is started and not dead, it is active * *@returnTrue if the thread is still alive; It is false; * /
public final native boolean isAlive(a);
Copy the code

step

public static void main(String[] args) throws InterruptedException {
    // Initialization state
    Thread t1 = new Thread(() -> {}); 
​
    // Runnable/running state, as for when running is controlled by the underlying platform
    t1.start(); 
​
    // Block, waiting for the T1 thread to finish executing
    t1.join();  
}
Copy the code
  • T1 new Thread(() -> {})

  • Call a t1. Start (); Starting a thread

  • Also call t1.join(); Method, the main thread enters the wait state

    • The core code in the Join method is the callwhile (isAlive())
    • isAlive()The method is to determine whether the T1 thread is finished
    • According to the above code, the thread is calling an empty method() - > {}
    • soisAlive()It’s going to go straight backfalse
    • At this timet1.join();Method within the code execution, will call the JVM C++ source, executenotifyAll()
    • The code returns to the main threadt1.join();So let’s go ahead and execute
    • The end of the

If the code is modified to

public static void main(String[] args) throws InterruptedException {
    // Initialization state
    Thread t1 = new Thread(() -> {
        try {
            Thread.sleep(1000);
        } catch(InterruptedException e) { e.printStackTrace(); }});// Runnable/running state, as for when running is controlled by the underlying platform
    t1.start(); 
​
    // Block, waiting for the T1 thread to finish executing
    t1.join();  
}
Copy the code

So when you call the t1.join() method

  • Create thread T1new Thread(() -> {//... })
  • callt1.start();Starting a thread
  • At the same time callt1.join();Method, main thread (main) will enter the wait state
    • The core code in the Join method is the callwhile (isAlive())
    • isAlive()The method is to determine whether the T1 thread is finished
    • Based on the code above, the method executed by the thread takes about a second to complete
    • soisAlive()It’s going to go straight backtrue, the callwait()Continue to wait for
    • inwhile (isAlive())It’ll be back in a second or sofalse
    • At this timet1.join();Method completes code execution
    • The code returns to the main threadt1.join();So let’s go ahead and execute
    • The end of the

conclusion

After looking at the source code, or relatively easy to understand. However, I could not find any place in the source code where notifyAll is called after executing the join. I searched the Internet for this question and found the final answer in the JVM C++ source code

void JavaThread::run(a) {...thread_main_inner(a); }void JavaThread::thread_main_inner(a) {...this->exit(false);
  delete this;
}
​
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {...ensure_join(this); . }static void ensure_join(JavaThread* thread) {
  // We do not need to grap the Threads_lock, since we are operating on ourself.
  Handle threadObj(thread, thread->threadObj());
  assert(threadObj.not_null(), "java thread object must exist");
  ObjectLocker lock(threadObj, thread);
  // Ignore pending exception (ThreadDeath), since we are exiting anyway
  thread->clear_pending_exception(a);// Thread is exiting. So set thread_status field in java.lang.Thread class to TERMINATED.
  java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
  // Clear the native thread instance - this makes isAlive return false and allows the join()
  // to complete once we've done the notify_all below
  java_lang_Thread::set_thread(threadObj(), NULL);
  lock.notify_all(thread); / / the key
  // Ignore pending exception (ThreadDeath), since we are exiting anyway
  thread->clear_pending_exception(a); }Copy the code

The key code is lock.notify_all(thread); This is where notifyAll() is called