Java并发—Join使用及原理
2021/8/1 22:06:03
本文主要是介绍Java并发—Join使用及原理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
一、理解
阅读JDK Join方法注释如下:
Waits for this thread to die.//等待该线程死亡
所以其作用是 等待该线程死亡。简单理解就是,调用该方法的线程阻塞,直到被调用Join的线程死亡。
我们回忆一下,线程的5种状态,如下:
- NEW:新建
- RUNNABLE:运行中
- BLOCKED:阻塞。等待锁,通常是通过synchronize
- WAITING:等待。通常是调用Object.wait(),Thread.join()
- TIMED_WAITING:时间等待
- TERMINATED:死亡
等待该线程死亡从状态角度看,就是等待其他状态转入死亡状态。
二、使用
2.1 Main线程等待子线程
我们先展示一个基本的例子,主线程等待子线程完成后执行。
public static void main(String[] args) { Thread thread1 = new Thread(new Runnable() { public void run() { System.out.println("子线程执行开始"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("子线程执行结束"); } }); try { System.out.println("主线程执行开始"); thread1.start(); thread1.join(); System.out.println("主线程执行完成"); } catch (InterruptedException e) { e.printStackTrace(); } }
2.2 T1、T2、T3依次等待
我们先构造T1、T2、T3线程,让它们分别等待6s、4s、2s。如果没有Join,执行完成顺序是T3、T2、T1,存在Join,则完成顺序为T1、T2、T3。
public static void main(String[] args) { final Thread t1 = new Thread(new Runnable() { public void run() { System.out.println("T1子线程执行开始"); try { //设置6秒,让T1最后执行完 Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("T1子线程执行结束"); } }); final Thread t2 = new Thread(new Runnable() { public void run() { System.out.println("T2子线程执行开始"); try { t1.join(); //设置4秒 Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("T2子线程执行结束"); } }); Thread t3 = new Thread(new Runnable() { public void run() { System.out.println("T3子线程执行开始"); try { t2.join(); //设置2秒 Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("T3子线程执行结束"); } }); t1.start(); t2.start(); t3.start(); }
三、原理
Object.join()首先调用join(long millis)。代码如下:
public final void join() throws InterruptedException { join(0);//直接调用重载方法,入参为0,也就是等待最大时间为0,持续阻塞 }
下面是join的核心代码,我们在方法中逐一讲解
public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis();//获得初入方法时间 long now = 0;//设置已经执行的时间,初始化为0 if (millis < 0) {//验证参数合法性,小于0不合法 throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) {//等于0,说明无最大超时时间 while (isAlive()) {//检查线程状态是否死亡,如果非死亡,则阻塞 wait(0);//wait(0)等同于wait(),会一直阻塞。直到线程死亡时,会调用notifyAll()通知所有阻塞线程 } } else { while (isAlive()) {检查线程状态是否死亡,如果非死亡,则执行 long delay = millis - now; //计算剩余等待时间,通过最大等待时间-已经执行的时间 if (delay <= 0) {//到达最大等待时间,则退出 break; } wait(delay);//等待 剩余时长 now = System.currentTimeMillis() - base;//每次循环执行,都会当前执行时间-初入方法时间,得到已经执行的时间 } } }
可以看到其核心就是检查Thread是否死亡状态,并通过wait()方法持续阻塞,直到线程死亡时,触发notifyAll。所以join的阻塞实际上也是通过native wait()方法实现的。
思考一
学习上面知识后,思考wait(0)即可实现一直等待,直到线程死亡,为什么外层需要while检查状态,而不使用if?
回答:因为目标Thread对象可能被其他线程持有,它们有可能调用notify()、notifyAll()接口,如果未使用while,会导致wait(0)被唤醒,join继续执行,而目标Thread仍未死亡。
这篇关于Java并发—Join使用及原理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-27消息中间件底层原理资料详解
- 2024-11-27RocketMQ底层原理资料详解:新手入门教程
- 2024-11-27MQ底层原理资料详解:新手入门教程
- 2024-11-27MQ项目开发资料入门教程
- 2024-11-27RocketMQ源码资料详解:新手入门教程
- 2024-11-27本地多文件上传简易教程
- 2024-11-26消息中间件源码剖析教程
- 2024-11-26JAVA语音识别项目资料的收集与应用
- 2024-11-26Java语音识别项目资料:入门级教程与实战指南
- 2024-11-26SpringAI:Java 开发的智能新利器