Java
Java多线程

Java多线程 06 - 线程的join

简介:join()方法可以让一个线程等待另一个线程执行完成。

1. join()介绍

join()定义在Thread.java中。join()在Oracle官方的文档中的定义如下:

The join method allows one thread to wait for the completion of another. If t is a Thread object whose thread is currently executing, t.join(); causes the current thread to pause execution until t’s thread terminates. Overloads of join allow the programmer to specify a waiting period. However, as with sleep, join is dependent on the OS for timing, so you should not assume that join will wait exactly as long as you specify.
Like sleep, join responds to an interrupt by exiting with an InterruptedException.

官方的解释是:join()方法可以让一个线程等待另一个线程执行完成。如果t是当前正在执行的线程,当调用t.join()操作会使当前线程暂停执行直到t的线程终止后恢复。

上面的解释其实还是很晦涩,其实我们需要理解的正在执行的线程当前线程的区别,这句话中,t是正在执行的线程,但其实不代表t就一定是处于运行状态的,t也有可能处于就绪、阻塞等状态,而当前线程指的是正处于运行状态的线程。有了这个区分,就好理解join()方法的作用了。假设t线程和主线程同时在执行,分下面两种情况:

  • 在单核单CPU环境中,t线程和主线程是交替执行的,此时假设主线程正在执行(那么t线程是等待状态),主线程调用了t.join()之后,主线程将暂停执行,t线程切换到运行状态;直到t线程执行完成,主线程才恢复执行。
  • 在多核或多CPU环境中,t线程和主线程可能是并行执行的,此时主线程调用了t.join()(这期间t线程还在并行执行),主线程将暂停执行,直到t线程执行完成,主线程才恢复执行。

Thread类中对join()方法的注释是Waits for this thread to die,翻译为“等待当前线程死亡”。

我们来看一个join()的例子:

  • public class JoinTest {
  • public static void main(String[] args) throws Exception {
  • Thread thread1 = new Thread(new Runnable() {
  • @Override
  • public void run() {
  • for (int i = 0; i < 5; i++) {
  • System.out.println(Thread.currentThread().getName() + " keep running " + i);
  • try {
  • Thread.sleep(500);
  • } catch (InterruptedException e) {
  • e.printStackTrace();
  • }
  • }
  • }
  • }, "thread-1");
  • Thread thread2 = new Thread(new Runnable() {
  • @Override
  • public void run() {
  • for (int i = 0; i < 5; i++) {
  • System.out.println(Thread.currentThread().getName() + " keep running " + i);
  • try {
  • Thread.sleep(1000);
  • } catch (InterruptedException e) {
  • e.printStackTrace();
  • }
  • }
  • }
  • }, "thread-2");
  • // 开启两个子线程
  • thread1.start();
  • thread2.start();
  • System.out.println(thread1.getName() + " begin execute join method");
  • // 主线程对thread1进行join
  • thread1.join();
  • System.out.println(Thread.currentThread().getName() + " running");
  • }
  • }

在示例代码中,分别创建了两个线程thread-1和thread-2,并启动了两个线程,然后对thread1进行了join操作,某一次运行的结果如下:

  • thread-1 keep running 0
  • thread-2 keep running 0
  • thread-1 begin execute join method
  • thread-1 keep running 1
  • thread-2 keep running 1
  • thread-1 keep running 2
  • thread-1 keep running 3
  • thread-2 keep running 2
  • thread-1 keep running 4
  • main running
  • thread-2 keep running 3
  • thread-2 keep running 4

从运行结果可以看出,主线程、thread1和thread2同时运行,在主线程中使用thread1进行join操作后,thread1和thread2还可以继续运行,而主线程则在thread1.join();这一行阻塞了,当thread1执行结束后,主线程才能继续执行。它们交替运行时序图如下:

2. join()源码分析

join操作是定义在Thread类中,它的源码如下(基于JDK1.7.0_07):

  • public final void join() throws InterruptedException {
  • join(0);
  • }
  • 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;
  • }
  • }
  • }

join()底层调用的是重载方法join(long),实现的原理其实是无限wait。在那个线程对象上调用join(),只要该线程对象是活的,调用该操作的线程就不停的等待。

注:wait()的作用是让当前线程等待,而这里的当前线程是指当前在CPU上运行的线程。所以,虽然是调用子线程的wait()方法,但是它是通过主线程去调用的;所以,休眠的是主线程,而不是子线程。