Thread.join源码分析

210 阅读3分钟

下面展示最基本的代码示例

main 为主线程

t1 为子线程

public static void main(String[] args) throws InterruptedException {
    // 初始化状态
    Thread t1 = new Thread(() -> {}); 
​
    // 可运行 / 运行状态,至于什么时候运行由底层平台控制
    t1.start(); 
​
    // 阻塞状态,等待t1线程执行完毕
    t1.join(); 
}

Thread.join() 源码分析

join有三个重载方法,分别为join()join(long millis)join(long millis, int nanos)

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

join()方法等价于join(0)

join(long millis, int nanos)方法最终也是调用join(long millis)

所以我们就拿join(long millis)来做介绍

/**
 * 最多等待millis毫秒该线程终止。超时为0则意味着永远等待。
 * 此方法使用循环方式判断isAlive()方式调用this.watit。当前程终止时,调用this.notifyAll方法。
 * 建议应用程序不要在Thread实例上使用wait、notify或notifyAll
 *
 * @param   millis - 等待时间(以毫秒为单位)
 * @throws  IllegalArgumentException - 如果millis的值为负
 * @throws  InterruptedException - 如果任何线程中断了当前线程。抛出此异常时清除当前线程的中断状态。
 */
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;
        }
    }
}

其中的核心方法就是

// while(isAlive())是为了防止子线程伪唤醒(spurious wakeup),
//只要子线程(t1)线程没有结束,父线程(main)就需要继续等下去。 
while (isAlive()) {
     wait(0);
 }

isAlive() 是一个 final 的 native方法

/**
 * 测试此线程是否存活。 如果线程已启动且尚未死亡,则该线程处于活动状态
 *
 * @return 如果此线程还活着,则为true; 否则为false;
 */
public final native boolean isAlive();

执行步骤

public static void main(String[] args) throws InterruptedException {
    // 初始化状态
    Thread t1 = new Thread(() -> {}); 
​
    // 可运行 / 运行状态,至于什么时候运行由底层平台控制
    t1.start(); 
​
    // 阻塞状态,等待t1线程执行完毕
    t1.join();  
}
  • 创建线程 t1 new Thread(() -> {})

  • 调用t1.start();启动线程

  • 同时调用t1.join();方法, 主线程(main)会进入等待状态

    • join方法中的核心代码就是调用 while (isAlive())
    • isAlive()方法就是判断t1线程是否执行结束
    • 根据上述代码来看,线程里面是调用空的方法() -> {}
    • 所以isAlive()会直接返回false
    • 此时t1.join();方法内的代码执行结束,会调用 jvm C++的源码,执行notifyAll()
    • 代码返回到主线程的t1.join();继续往下执行
    • 结束

如果代码修改为

public static void main(String[] args) throws InterruptedException {
    // 初始化状态
    Thread t1 = new Thread(() -> {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
​
    // 可运行 / 运行状态,至于什么时候运行由底层平台控制
    t1.start(); 
​
    // 阻塞状态,等待t1线程执行完毕
    t1.join();  
}

那么在调用t1.join()方法时

  • 创建线程 t1 new Thread(() -> {//...})
  • 调用t1.start();启动线程
  • 同时调用t1.join();方法, 主线程(main)会进入等待状态
    • join方法中的核心代码就是调用 while (isAlive())
    • isAlive()方法就是判断t1线程是否执行结束
    • 根据上述代码来看,线程执行的方法要1秒钟左右才回执行结束
    • 所以isAlive()会直接返回true,调用wait()继续等待
    • while (isAlive())一秒左右后会返回false
    • 此时t1.join();方法内的代码执行结束
    • 代码返回到主线程的t1.join();继续往下执行
    • 结束

总结

在看完源码之后,还是比较容易理解的。 但是我在源码中没有找到代码在执行完join后调用了notifyAll的地方,带着这个问题在网上找了一下才发现,最终答案在jvm C++源码里

void JavaThread::run() {
  ...
  thread_main_inner();
}
​
void JavaThread::thread_main_inner() {
  ...
  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();
  // 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); // 重点
  // Ignore pending exception (ThreadDeath), since we are exiting anyway
  thread->clear_pending_exception();
}

其中的重点代码就是lock.notify_all(thread);这里就是调用notifyAll()的地方了