Thread.join() 源码全面解析

先看文档

Waits at most millis milliseconds for this thread to die. A timeout of 0 means to wait forever.
This implementation uses a loop of this.wait calls conditioned on this.isAlive. As a thread terminates the this.notifyAll method is invoked. It is recommended that applications not use wait, notify, or notifyAll on Thread instances.
翻译如下:
在指定时间内等待线程终结,0代表一直等待,此实现使用在this.isAlive()条件上的this.wait调用。线程终结后将会调用this.notifyAll(),建议应用程序不要在线程实例上调用wait,notify,notifyAll

上源码:

    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;
            }
        }
    }

在一个线程中调用另一个线程的thread.join()方法,最终会由上面代码片段里的逻辑来处理。
下面我们来对这段代码进行分析,去除其它逻辑,只剩下核心逻辑之后是这样的:

    public final synchronized void join(long millis)
    throws InterruptedException {
        while (isAlive()) {
            wait(0);
        }
    }
  • synchronized 关键字很关键,表明此方法是线程安全的,线程进入此方法需要获取锁。
    那么这里的锁是什么呢?是本线程实例对象,也就是语法上synchronized 关键字省略(this)的写法。
  • InterruptedException 说明此过程可以被打断,如有必要需要处理InterruptedException异常
  • isAlive()判断线程的存活状态,也就是不能是终结态TERMINATED,是一个final 的 native方法
public final native boolean isAlive();
  • while,而不是if,这里是为了防上虚假唤醒,虚假唤醒是操作系统的权衡性能和健壮性之后的处理方案,具体参考虚假唤醒(spurious wakeup)
  • wait(0)方法,表示释放锁,线程进入等待状态

关键点梳理完了,下面讲理一下核心代码运行逻辑

线程A调用线程B.join()方法,重载方法调用到join(long millis)方法
循环检查条件线程B.isAlive(),如果是存活状态则释放锁,线程A进入等待状态,等待线程B执行完成
为防止虚假唤醒,使用while循环方式来检查条件
如果线程B终结了,则跳出while循环,join方法执行结束,回到线程A继续执行

看到这里有两点需要注意:

  • 线程B的join方法里wait方法之后,锁被释放了,此时其它线程若有调用线程B.join()方法,可正常获取锁,若不能获取锁则在锁上等待,说明多个线程可等待某一线程执行结束
  • 当线程B达到终结态后,跳出while循环,join方法执行结束,那么问题来了,请问线程A何时可以继续执行?答:因为释放了锁,所以只能等到notify通知之后才能继续执行,但是从上面上源代码里并没有看到notify调用啊。而且文档上表明不建议应用程序自己去调用,最终答案在jvm C++源码里,源码之下,了无秘密
    jdk8u/hotspot/src/share/vm/runtime/thread.cpp
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) {
  ...
  // Notify waiters on thread object. This has to be done after exit() is called
  // on the thread (if the thread is the last thread in a daemon ThreadGroup the
  // group should have the destroyed bit set before waiters are notified).
  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();
}

重点看ensure_join 方法里的 lock.notify_all(thread);语句 代码说明jvm会在线程B执行完成后帮我们调用notify_all()方法,这样线程A就会重回Runnable状态,时间片分配后线程A可执行join方法之后的代码。
这就是**《Java并发编程的艺术》**中讲到,当线程终止时,会调用自身的notifyAll方法,会通知所有等待在该线程对象上的线程。

至此,分析完毕。

你可能感兴趣的:(Java)