【Java多线程】Thread.join()原理

目录

1 前言

2 使用场景

3 案例

4 源码解析

5 Thread.currentThread()

6 执行过程


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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值