1.4 等待线程执行终止的join方法

在项目实践中经常会遇到一个场景,就是需要等待某几件事情完成后才能继续往下执行,比如多个线程加载资源,需要等待多个线程全部加载完毕再汇总处理。Thread类中有一个join方法就可以做这个事情,前面介绍的等待通知方法是Object类中的方法,而join方法则是Thread类直接提供的。join是无参且返回值为void的方法。下面来看一个简单的例子。

public static void main(String[] args) throws InterruptedException {
        Thread threadOne = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("child threadOne over! ");
            }
        });
        Thread threadTwo = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("child threadTwo over! ");
            }
        });
        //启动子线程
        threadOne.start();
        threadTwo.start();
        System.out.println("wait all child thread over! ");
        //等待子线程执行完毕,返回
        threadOne.join();
        threadTwo.join();
        System.out.println("all child thread over! ");
    }

如上代码在主线程里面启动了两个子线程,然后分别调用了它们的join()方法,那么主线程首先会在调用threadOne.join()方法后被阻塞,等待threadOne执行完毕后返回。threadOne执行完毕后threadOne.join()就会返回,然后主线程调用threadTwo.join()方法后再次被阻塞,等待threadTwo执行完毕后返回。这里只是为了演示join方法的作用,在这种情况下使用后面会讲到的CountDownLatch是个不错的选择。

另外,线程A调用线程B的join方法后会被阻塞,当其他线程调用了线程A的interrupt()方法中断了线程A时,线程A会抛出InterruptedException异常而返回。下面通过一个例子来加深理解。

    public static void main(String[] args) throws InterruptedException {
        //线程one
        Thread threadOne = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("threadOne begin run! ");
                for (; ; ) {
                }
            }
        });
      //获取主线程
        final Thread mainThread = Thread.currentThread();
        //线程two
        Thread threadTwo = new Thread(new Runnable() {
            @Override
            public void run() {
                //休眠1s
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //中断主线程
                mainThread.interrupt();
            }
        });
        // 启动子线程
        threadOne.start();
        //延迟1s启动线程
        threadTwo.start();
        try{//等待线程one执行结束
            threadOne.join();
        }catch(InterruptedException e){
            System.out.println("main thread:" + e);
        }
    }

输出结果如下。

如上代码在threadOne线程里面执行死循环,主线程调用threadOne的join方法阻塞自己等待线程threadOne执行完毕,待threadTwo休眠1s后会调用主线程的interrupt()方法设置主线程的中断标志,从结果看在主线程中的threadOne.join()处会抛出InterruptedException异常。这里需要注意的是,在threadTwo里面调用的是主线程的interrupt()方法,而不是线程threadOne的。