多线程 join 方法

多线程 join 方法

1.前言
本节对 join 方法进行深入的剖析,主要内容点如下:

  • 了解 join 方法的作用,初步的理解 join 方法的使用带来的效果是学习本节内容的基础;
  • 了解 join 方法异常处理,我们在使用 join 方法是,需要对 join 方法进行有效的异常处理;
  • 通过匿名内部类创建Thread 是我们本节代码示例所使用的的方式,对这种方式的掌握在后续工作中非常重要;
  • 掌握 join 方法如何使用,这是本节的重点内容,也是本节的核心内容;
  • 掌握带参数的 join 方法使用,在后续开发过程中,如果对接口的最大返回时间有要求的话,某些情况下会用到带参数的 join
    方法,次重点内容。

2. join 方法的作用
方法定义: 多线程环境下,如果需要确保某一线程执行完毕后才可继续执行后续的代码,就可以通过使用 join 方法完成这一需求设计。

在项目实践中经常会遇到一个场景,就是需要等待某几件事情完成后主线程才能继续往下执行, 比如多个线程加载资源, 需要等待多个线程全部加载完毕再汇总处理。

Thread 类中有一个 join 方法就可以做这个事情,join 方法是 Thread 类直接提供的。join 是无参且返回值为 void 的方法。
在这里插入图片描述
如上图所示,假如有 3 个线程执行逻辑,线程 1 需要执行5秒钟,线程 2 需要执行10 秒钟,线程 3 需要执行 8 秒钟。 如果我们的开发需求是:必须等 3 条线程都完成执行之后再进行后续的代码处理,这时候我们就需要使用到 join 方法。
使用 join 方法后:

第 5 秒钟: 线程 1 执行完毕;线程 2 执行了一半; 线程 3 还差 3 秒执行完毕;
第 8 秒钟: 线程 1 等待了 3 秒; 线程 3 执行完毕; 线程 2 还差 2 秒执行完毕;
第10 秒钟: 线程 1 等待了 5 秒; 线程 3 等待了 2 秒;线程 2 执行完毕;
从线程 2 执行结束的那一刻:三条线程同时进行后续代码的执行。

这就是 join 方法的作用与解释。
3. join 方法异常处理
join 方法是 Thread 类中的方法,为了了解该方法的异常处理,我们先来简要的看下 join 方法的 JDK 源码:

public final void join() throws InterruptedException {
        join(0);
}

从源代码中我们可以看到, join 方法抛出了异常:
throws InterruptedException
所以,我们在使用 join 方法的时候,需要对 join 方法的调用进行 try catch 处理或者从方法级别进行异常的抛出。
try-catch 处理示例:

public class DemoTest implements Runnable{
    @Override
    public void run() {
        System.out.println("线程:"+Thread.currentThread()+" 正在执行...");
    }
    public static void main(String[] args) {
        Thread t1 = new Thread(new DemoTest());
        t1. start();
        try {
            t1.join();
        } catch (InterruptedException e) {
            // 异常捕捉处理
        }
    }
}

throws 异常处理示例:

public class DemoTest implements Runnable throws InterruptedException {
    @Override
    public void run() {...}
    public static void main(String[] args) {
        Thread t1 = new Thread(new DemoTest());
        t1. start();
        t1.join();
    }
}

4. join 方法如何使用
为了更好的了解 join 方法的使用,我们首先来设计一个使用的场景。
场景设计:

线程 1 : 执行时间 5 秒钟;
线程 2 : 执行时间 10 秒钟;
线程 3: 执行 8 秒钟。
需求: 我们需要等 3 个线程都执行完毕后,再进行后续代码的执行。3 个线程执行完毕后,请打印执行时间。

期望结果: 10 秒执行时间。
看到这个是不是似曾相识呢? 这就是我们本节第 2 知识点所举出的示例,现在我们来进行代码实现和验证,体会 join 方法的使用。
实例:

public class DemoTest{
    public static void main(String[] args) throws InterruptedException {
        Thread threadOne = new Thread(new Runnable() { //线程 1
            @Override
            public void run() {
                try {
                    Thread.sleep (5000 ); //线程 1 休眠 5 秒钟
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println ("线程 1 休眠 5 秒钟,执行完毕。");
            }
        });

        Thread threadTwo = new Thread(new Runnable() { //线程 2
            ...
                    Thread.sleep (10000 ); //线程 2 修眠 10 秒钟
            ...
                System.out.println ("线程 2 修眠 10 秒钟,执行完毕。");
            }
        });

        Thread threadThree = new Thread(new Runnable() {//线程 3
            ...
                    Thread.sleep (8000 ); //线程 3 修眠 8 秒钟
            ...
                System.out.println ("线程 3 修眠 8 秒钟,执行完毕。");
            }
        });

        Long startTime = System.currentTimeMillis();
        threadOne. start();threadTwo. start();threadThree. start();
        System.out.println("等待三个线程全部执行完毕再继续向下执行,我要使用 join 方法了。");
        threadOne.join(); //线程 1 调用 join 方法
        threadTwo.join(); //线程 2 调用 join 方法
        threadThree.join(); //线程 3 调用 join 方法
        Long endTime = System.currentTimeMillis();
        System.out.println("三个线程都执行完毕了,共用时: "+ (endTime - startTime) + "毫秒");
    }
}

执行结果验证:

等待三个线程全部执行完毕再继续向下执行,我要使用 join 方法了。
线程 1 休眠 5 秒钟,执行完毕。
线程 3 修眠 8 秒钟,执行完毕。
线程 2 修眠 10 秒钟,执行完毕。
三个线程都执行完毕了,共用时: 10002毫秒

从执行的结果来看,与我们对 join 方法的理解和分析完全相符。
5. 带参数的 join 方法使用
除了无参的 join 方法以外, Thread 类还提供了有参 join 方法如下:

public final synchronized void join(long millis)
    throws InterruptedException

该方法的参数 long millis 代表的是毫秒时间。

方法作用描述:等待 millis 毫秒终止线程,假如这段时间内该线程还没执行完,也不会再继续等待。
结合上一个知识点的代码,我们都是调用的无参 join 方法,现在对上一个知识点代码进行如下调整:

threadOne.join(); //线程 1 调用 join 方法
threadTwo.join(3000); //线程 2 调用 join 方法
threadThree.join(); //线程 3 调用 join 方法


从代码中我们看到,线程 2 使用 join 方法 3000 毫秒的等待时间,如果 3000 毫毛后,线程 2 还未执行完毕,那么主线程则放弃等待线程 2,只关心线程 1 和线程 3。
我们来看下执行结果:

等待三个线程全部执行完毕再继续向下执行,我要使用 join 方法了。
线程 1 休眠 5 秒钟,执行完毕。
线程 3 修眠 8 秒钟,执行完毕。
三个线程都执行完毕了,共用时: 8000毫秒
线程 2 修眠 10 秒钟,执行完毕。


从执行结果来看, 总用时 8000 毫秒,因为线程 2 被放弃等待了,所以只考虑线程 1 和线程 3 的执行时间即可。

6. 小结
在实际的开发场景中,经常会设计到对 join 方法的使用,无参方法使用更加常见,了解 join 方法的使用非常重要。

本节重中之重,就是掌握 join 方法的使用,join 方法在后续的开发工作中非常关键,很多情况下都会有所使用。

  • 12
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值