目录
1 前言
如果一个线程A执行了Thread.join()方法,含义是,当前线程A等待Thread线程终止之后才从Thread.join()返回。线程Thread除了Thread.join(),还有Thread.join(long millis)和Thread.join(long millis, int nanos)两个具备超时特性的方法。表示如果线程thrad在给定时间内没有终止,那么将会从超时方法中返回。
2 使用场景
在很多情况下,主线程生成并启动了子线程,如果子线程里面要进行大量的耗时运算,主线程往往将结束于子线程之前。但如果主线程处理完其他事务之后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就需要用到join()
方法。
3 案例
下面代码创建了10个线程,编号0~9,每个线程执行run方法前调用前一个线程的join()方法,也就是线程0结束了,线程1才能从join()方法中返回,而线程0需要等main线程结束。这样一来,各个线程就是按顺序执行的,前一个线程执行完,后一个线程才能开始。
public class Join{
public static void main(String[] args) throws InterruptedException {
Thread previous = Thread.currentThread();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(new Domino(previous), String.valueOf(i));
thread.start();
previous = thread;
}
TimeUnit.SECONDS.sleep(5);
System.out.println(Thread.currentThread().getName() + "terminate.");
}
static class Domino implements Runnable {
private Thread thread;
public Domino(Thread thread) {
this.thread = thread;
}
@Override
public void run() {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "terminate.");
}
}
}
// 输出结果如下
// mainterminate.
// 0terminate.
// 1terminate.
// 2terminate.
// 3terminate.
// 4terminate.
// 5terminate.
// 6terminate.
// 7terminate.
// 8terminate.
// 9terminate.
4 源码解析
下面的代码创建并启动了两个线程:previousThread和currThread。currThread的run()方法执行了previousThread.join()。
final Thread previousThread = new Thread("previousThread"); // previousThread
previousThread.start();
new Thread(new Runnable() { // currThread
public void run() {
try {
previousThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"currThread").start();
针对上面的代码,我们来看Thread.join()的源码到底做了什么。下面是join()方法的核心部分,isAlive()判断当前线程previousThread是否存活(执行结束),wait(0)方法使Thread.currentThread()进入WAITING状态,注意上面场景中Thread.currentThread()是currThread。
public final synchronized void join(long millis) throws InterruptedException {
...
while (isAlive()) {
wait(0);
}
...
}
5 Thread.currentThread()
在某个普通方法中执行Thread.currentThread(),结果是执行这个方法的线程,如main线程。run()和main()方法一样,代表“线程级”方法,在run()方法执行Thread.currentThread(),结果就是这个run()方法所属的线程。下面是一个简单的测试例子:
public class Test2 {
public static void main(String[] args) {
MyThread2 myThread2 = new MyThread2("myThread");
myThread2.start();
myThread2.printThreadName();
}
}
class MyThread2 extends Thread{
MyThread2(String name){ super(name); }
@Override
public void run(){
System.out.println("run方法中调用:"+Thread.currentThread().getName()); // myThread
}
public synchronized void printThreadName() {
System.out.println("main方法中调用:"+Thread.currentThread().getName()); // main
}
}
打印结果为:
main方法中调用:main
run方法中调用:myThread
6 执行过程
我们以第4点中的案例来介绍join()的执行过程。
-
join()被synchronized修饰,所以currThread首先要获取锁,即previousThread对象。若此时其它线程中也执行了previousThread.join(),显然它们会因为获取不到锁而阻塞在join()上。
-
若thread.join()中的thread提前结束或根本没启动,isAlive()方法会直接返回false,使得join()返回,currThread继续执行。
-
若执行到previousThread.join()时previousThread还在运行,currThread会因为wait(0)陷入等待,同时释放锁,参与竞争的某个线程会被唤醒,然后继续因为previousThread还存活而陷入等待。
-
当previousThread执行完毕后,会执行notifyAll()来唤醒所有阻塞在自身对象上的线程,此时所有线程被唤醒,同时检测到isAlive()为false,从join()中返回。
关于线程执行完毕后会调用notifyAll(),openjdk源码中有代码,下面的内容摘自论坛回答:
当 Thread 执行完毕之后会调用 notifyAll()
方法,不过不是在 Java
源码中调用的,而是在 jdk
的 native code 里调用的。
openjdk 7
的源码里有:
/jdk7/hotspot/src/os/linux/vm/os_linux.cpp
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);
static void *java_start(Thread *thread) {
...
thread->run();
return 0;
}
参数里的 thread
其实是 JavaThread
的实例,而在 JavaThread
的内部实现中,在 run
方法执行结束之前会调用 lock.notify_all(thread)
通知所有 join
等待的线程。源码如下:
/jdk7/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); // 就在此处调用的notifyAll()
// Ignore pending exception (ThreadDeath), since we are exiting anyway
thread->clear_pending_exception();
}