Java Thread join 详解

Thread.join()挂起当前线程,直到目标线程结束,当前线程被唤醒。底层源码在线程exit的时候,通过ensure_join唤醒等待该线程的waiter。

Thread join 例子

需求:一个线程去干点活,等工作结束后,通知主线程。

public static void main(String[] args) {
    Thread t = new Thread(() -> {
        System.out.println("I am doing my job!");
        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("job is done");
    });
    t.setName("my-job-thread");    
    t.start();
    System.out.println("all is done");
}

输出

all is done
I am doing my job!
job is done

显然,不符合要求。

其实只要增加一行代码即可:

public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread(() -> {
        System.out.println("I am doing my job!");
        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("job is done");
    });
    t.setName("my-job-thread");
    t.start();
    // 划重点
    t.join();
    System.out.println("all is done");
}

输出

I am doing my job!
job is done
all is done

Thread join 原理

加上join()方法后,main线程等待线程t结束后才继续执行。 如果jstack做thread dump:

thread-join.png

main线程被挂起,等待唤醒。

Thread.join()的说明很简单明了:等待线程死亡。

/**
* Waits for this thread to die.
*/
public final void join() throws InterruptedException {
    join(0);
}

再看看实际调用的join(long millis):

/**
* 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.
*/
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;
        }
    }
}

/*
* Tests if this thread is alive. A thread is alive if it has been started and has not yet died.
*/
public final native boolean isAlive();
  1. join(long millis)被synchronized修饰。实例方法级别的synchronized,表明要获取这个实例的监视器锁。因为里面的方法使用了wait(),因此要先获取监视器锁。
  2. isAlive()返回线程是否死亡。
  3. 只要线程还活着,就wait(0)一直等待。
  4. 通常wait要通过notify来唤醒,但是t线程死亡后,自动唤醒了main线程,why?

ps. 关于synchronized、wait、notify,可以参见:

明明是main线程调用t线程的join方法,好像要挂起的应该是t线程,但实际挂起的是main线程,有点反直觉? 其实不然。看看Object.wait()的javadoc

Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.

重点是current thread
sychronized只是为了获取监视器锁而已,挂起的是当前线程(上面例子,就是main线程)。

最大的疑问是,t线程死亡后,为什么能够唤醒main线程?Java代码明明没有使用notify啊。 java代码没有notify,那么可能就是底层jvm代码做了这个操作。 最终找到 openjdk8 thread.cpp

void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
// more code

  // 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);

// more code
}

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

线程exit的时候,会调用ensure_join()通知这个线程对象等待队列的waiter。ensure_join最终调用notify_all唤醒waiter。 因此,t线程结束,main线程被唤醒。

Thread join 一个细节

join上的注释写明:

It is recommended that applications not use wait, notify, or notifyAll on Thread instances.

因为使用了wait机制,因此当前线程最好使用wait,notfiy等方法。 Thread.join使用了while检查,避免虚假唤醒

while (isAlive()) {
    wait(0);
}

Thread join 小结

Thread.join()挂起当前线程。目标线程结束后再唤醒当前线程。 可以用于简单的线程等待。 最好使用FutureTask.get()之类的方法代替。

Built with Hugo
Theme Stack designed by Jimmy