当前位置: 首页 > news >正文

05. Java多线程 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{@Overridepublic 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 {@Overridepublic 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@Overridepublic 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 方法在后续的开发工作中非常关键,很多情况下都会有所使用。

相关文章:

  • c++设计模式之一创建型模式
  • 1964springboot VUE 智慧社区可视化平台系统开发mysql数据库web结构java编程计算机网页源码maven项目
  • 问题解决:Problem exceeding maximum token in azure openai (with java)
  • 分布式光纤测温DTS使用的单模光纤与多模光纤有何区别?
  • Leetcode - 周赛401
  • 八大经典排序算法
  • kali中安装docker
  • SARscape——Lee滤波
  • celery使用 Zookeeper 或 kafka 作为broker,使用 mysql 作为 backend
  • POSTMAN接口详解
  • CentOS Linux 7系统中离线安装MySQL5.7步骤
  • Zabbix 7.0 LTS新特征
  • xss-lab靶场level1-level10
  • Centos7升级K8S集群
  • YOLOv10改进 | Neck | 添加双向特征金字塔BiFPN【含二次独家创新】
  • 《微软的软件测试之道》成书始末、出版宣告、补充致谢名单及相关信息
  • 【347天】每日项目总结系列085(2018.01.18)
  • 2018一半小结一波
  • Android Volley源码解析
  • Centos6.8 使用rpm安装mysql5.7
  • isset在php5.6-和php7.0+的一些差异
  • learning koa2.x
  • Meteor的表单提交:Form
  • MySQL QA
  • nodejs实现webservice问题总结
  • Python代码面试必读 - Data Structures and Algorithms in Python
  • windows下mongoDB的环境配置
  • 后端_ThinkPHP5
  • 简单基于spring的redis配置(单机和集群模式)
  • 讲清楚之javascript作用域
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 通信类
  • 突破自己的技术思维
  • 想使用 MongoDB ,你应该了解这8个方面!
  • 学习笔记:对象,原型和继承(1)
  • 云大使推广中的常见热门问题
  • AI算硅基生命吗,为什么?
  • 从如何停掉 Promise 链说起
  • 关于Android全面屏虚拟导航栏的适配总结
  • ​LeetCode解法汇总2182. 构造限制重复的字符串
  • ###项目技术发展史
  • #微信小程序:微信小程序常见的配置传值
  • (06)Hive——正则表达式
  • (2015)JS ES6 必知的十个 特性
  • (2024)docker-compose实战 (9)部署多项目环境(LAMP+react+vue+redis+mysql+nginx)
  • (4)logging(日志模块)
  • (免费领源码)Java#Springboot#mysql农产品销售管理系统47627-计算机毕业设计项目选题推荐
  • (实战)静默dbca安装创建数据库 --参数说明+举例
  • (原創) 物件導向與老子思想 (OO)
  • (转)PlayerPrefs在Windows下存到哪里去了?
  • .net core控制台应用程序初识
  • .NET Framework 3.5中序列化成JSON数据及JSON数据的反序列化,以及jQuery的调用JSON
  • .NET/C# 项目如何优雅地设置条件编译符号?
  • .net8.0与halcon编程环境构建
  • .net安装_还在用第三方安装.NET?Win10自带.NET3.5安装