Java多线程中关于join的问题?

代码一:

public static void main(String[] args) throws Exception {
    Thread thread = new Thread(() -> {
      try {
        TimeUnit.SECONDS.sleep(10);
        System.out.println("thread-0 thread exit.");
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }, "thread-0");
    thread.start();
    thread.join();
    System.out.println("main thread exit.");
  }

控制台(10s后打印并退出):

thread-0 thread exit.
main thread exit.

Process finished with exit code 0

代码二:

public static void main(String[] args) throws Exception {
    Thread.currentThread().join();
    System.out.println("main thread exit.");
  }

控制台(一直等待中):


问题:第一段代码是main线程阻塞在thread-0线程上,当thread-0执行完毕,main线程也同时退出,那第二段代码中main线程阻塞在哪个线程上呢,为什么没有退出?

阅读 2.8k
3 个回答

探究

public static void main(String[] args) throws Exception {
    Thread.currentThread().join();
    System.out.println("main thread exit.");
  }

为了了解问题本质,我们跟进去代码看看,线程的join方法如下:

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方法最终是待用了wait(0);这行代码,而wait方法是本地方法.

根据wait的方法定义,有以下几点逐一说明:

  • wait()方法在Object类定义,调用时会获取该对象上的监视器,因此必须在同步代码块上调用.从代码来看`public final synchronized void join(long millis)
    throws InterruptedException`,因此调用wait方法后,实际上是获取了当前对象(根据调试查看到是main线程)的监视器.
  • 根据wait方法的定义,它只在在如下几种情况才会被唤醒,否则将一直等待:
<ul>
  
<li>Some other thread invokes the {@code notify} method for this
      object and thread <var>T</var> happens to be arbitrarily chosen as
      the thread to be awakened.
     
<li>Some other thread invokes the {@code notifyAll} method for this
      object.
      
<li>Some other thread {@linkplain Thread#interrupt() interrupts}
      thread <var>T</var>.
      
<li>The specified amount of real time has elapsed, more or less.  If
      {@code timeout} is zero, however, then real time is not taken into
      consideration and the thread simply waits until notified.
</ul>

总结

因此,回答你的问题就是,join方法实质是调用了wait方法.wait方法调用后阻塞(我不认为这是阻塞)在当前线程(main线程上),根据wait的定义,不调用notify,notifyAll,interrupt以及超时机制(本例调用的是wait(0),故不存在这种情况),那么线程将一直处于等待状态,故一直不会退出。

main线程阻塞自己

大致的说下吧,Thread中,join()方法的作用是调用线程等待该线程完成后,才能继续用下运行。

public static void main(String[] args) throws InterruptedException
    {
        System.out.println("main start");

        Thread t1 = new Thread(new Worker("thread-1"));
        t1.start();
        t1.join();
        System.out.println("main end");
    }

在上面的例子中,main线程要等到t1线程运行结束后,才会输出“main end”。如果不加t1.join(),main线程和t1线程是并行的。而加上t1.join(),程序就变成是顺序执行了。

我们在用到join()的时候,通常都是main线程等到其他多个线程执行完毕后再继续执行。其他多个线程之间并不需要互相等待。

下面这段代码并没有实现让其他线程并发执行,线程是顺序执行的。

public static void main(String[] args) throws InterruptedException
    {
        System.out.println("main start");

        Thread t1 = new Thread(new Worker("thread-1"));
        Thread t2 = new Thread(new Worker("thread-2"));
        t1.start();
        //等待t1结束,这时候t2线程并未启动
        t1.join();
        
        //t1结束后,启动t2线程
        t2.start();
        //等待t2结束
        t2.join();

        System.out.println("main end");
    }

所以就会是这个结果

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题