本文分享自华为云社区《【高并发系列】Java中线程到底是按什么顺序执行的?你了解的可能是错误的!》,原文作者:冰 河 。

Java中线程的执行顺序可能与你想的不一样!一文解密多线程的执行顺序,调用Thread的start()方法启动线程时,线程的执行顺序是不确定的。也就是说,在同一个方法中,连续创建多个线程后,调用线程的start()方法的顺序并不能决定线程的执行顺序。

线程的执行顺序是不确定的

调用Thread的start()方法启动线程时,线程的执行顺序是不确定的。也就是说,在同一个方法中,连续创建多个线程后,调用线程的start()方法的顺序并不能决定线程的执行顺序。

例如,这里,看一个简单的示例程序,如下所示。

package io.binghe.concurrent.lab03;/*** @author binghe* @version 1.0.0* @description 线程的顺序,直接调用Thread.start()方法执行不能确保线程的执行顺序*/
public class ThreadSort01 {public static void main(String[] args){Thread thread1 = new Thread(() -> {System.out.println("thread1");});Thread thread2 = new Thread(() -> {System.out.println("thread2");});Thread thread3 = new Thread(() -> {System.out.println("thread3");});thread1.start();thread2.start();thread3.start();}
}

在ThreadSort01类中分别创建了三个不同的线程,thread1、thread2和thread3,接下来,在程序中按照顺序分别调用thread1.start()、thread2.start()和thread3.start()方法来分别启动三个不同的线程。

那么,问题来了,线程的执行顺序是否按照thread1、thread2和thread3的顺序执行呢?运行ThreadSort01的main方法,结果如下所示。

thread1
thread2
thread3

再次运行时,结果如下所示。

thread1
thread3
thread2

第三次运行时,结果如下所示。

thread2
thread3
thread1

注意:每个人运行的情况可能都不一样。

可以看到,每次运行程序时,线程的执行顺序可能不同。线程的启动顺序并不能决定线程的执行顺序。

如何确保线程的执行顺序

1.确保线程执行顺序的简单示例

在实际业务场景中,有时,后启动的线程可能需要依赖先启动的线程执行完成才能正确的执行线程中的业务逻辑。此时,就需要确保线程的执行顺序。那么如何确保线程的执行顺序呢?

可以使用Thread类中的join()方法来确保线程的执行顺序。例如,下面的测试代码。

package io.binghe.concurrent.lab03;
/*** @author binghe* @version 1.0.0* @description 线程的顺序,Thread.join()方法能够确保线程的执行顺序*/
public class ThreadSort02 {public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread(() -> {System.out.println("thread1");});Thread thread2 = new Thread(() -> {System.out.println("thread2");});Thread thread3 = new Thread(() -> {System.out.println("thread3");});thread1.start();//实际上让主线程等待子线程执行完成thread1.join();thread2.start();thread2.join();thread3.start();thread3.join();}
}

可以看到,ThreadSort02类比ThreadSort01类,在每个线程的启动方法下面添加了调用线程的join()方法。此时,运行ThreadSort02类,结果如下所示。

thread1
thread2
thread3

再次运行时,结果如下所示。

thread1
thread2
thread3

第三次运行时,结果如下所示。

thread1
thread2
thread3

可以看到,每次运行的结果都是相同的,所以,使用Thread的join()方法能够保证线程的先后执行顺序。

2.join方法如何确保线程的执行顺序

既然Thread类的join()方法能够确保线程的执行顺序,我们就一起来看看Thread类的join()方法到底是个什么鬼。

进入Thread的join()方法,如下所示。

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

可以看到join()方法调用同类中的一个有参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;}}
}

可以看到,有一个long类型参数的join()方法使用了synchroinzed修饰,说明这个方法同一时刻只能被一个实例或者方法调用。由于,传递的参数为0,所以,程序会进入如下代码逻辑。

if (millis == 0) {while (isAlive()) {wait(0);}
}

首先,在代码中以while循环的方式来判断当前线程是否已经启动处于活跃状态,如果已经启动处于活跃状态,则调用同类中的wait()方法,并传递参数0。继续跟进wait()方法,如下所示。

public final native void wait(long timeout) throws InterruptedException;

可以看到,wait()方法是一个本地方法,通过JNI的方式调用JDK底层的方法来使线程等待执行完成。

需要注意的是,调用线程的wait()方法时,会使主线程处于等待状态,等待子线程执行完成后再次向下执行。也就是说,在ThreadSort02类的main()方法中,调用子线程的join()方法,会阻塞main()方法的执行,当子线程执行完成后,main()方法会继续向下执行,启动第二个子线程,并执行子线程的业务逻辑,以此类推。

总结

Java中多线程并发的执行顺序历来是面试中的重点,掌握Java中线程的执行顺序不仅能够在面试中让你脱颖而出,更能够让你在平时的工作中,迅速定位由于多线程并发问题导致的“诡异”问题,让你从工作中脱颖而出。

点击关注,第一时间了解华为云新鲜技术~

面试官:Java中线程是按什么顺序执行的?相关推荐

  1. 惊艳面试官-Java中关于随机数生成8种方式的思考

    Java中生成随机数常用的有下面这8种写法:简而言之,名称带安全的未必安全,名字简洁的未必简单. Math.random() Random ThreadLocalRandom SecureRandom ...

  2. 面试官 | Java中的注解是如何工作的?

    自Java5.0版本引入注解之后,它就成为了Java平台中非常重要的一部分.开发过程中,我们也时常在应用代码中会看到诸如@Override,@Deprecated这样的注解.这篇文章中,我将向大家讲述 ...

  3. java中线程死锁及避免_如何避免Java线程中的死锁?

    java中线程死锁及避免 如何避免Java中的死锁? 是Java面试中最受欢迎的问题之一,也是本季多线程的风格,主要是在高层提出,并带有很多后续问题. 尽管问题看起来很基础,但是一旦您开始深入研究,大 ...

  4. 吊打面试官 | Java到底是值传递还是引用传递

    HI ! 我是小小,我们又见面了,今天是本周的第六篇,这篇主要讲解关于Java到底是值传递还是引用传递. 先说答案 答案为值传递 答案解释 先说一下值传递和引用传递的概念和区别 值传递:是指在调用函数 ...

  5. 2020我也当了面试官-java面试题之一

    2020我也当了面试官-java面试题之一 写在前面   最近一段时间开始帮团队招聘一些1-3年的java开发人员,参与几场下来,发现有些面试官问的问题虽然基础,我却记忆有些模糊.大概是平常CRUD太 ...

  6. JAVA中线程同步的方法(7种)汇总

    JAVA中线程同步的方法(7种)汇总 同步的方法: 一.同步方法 即有synchronized关键字修饰的方法. 由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法. ...

  7. java中线程的生命周期

    文章目录 java中Thread的状态 NEW Runnable BLOCKED WAITING TIMED_WAITING TERMINATED java中线程的生命周期 线程是java中绕不过去的 ...

  8. java中线程的6种状态

    java中线程的状态分为6种. 1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法. 2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running ...

  9. Java中线程池,你真的会用吗

    转载自   Java中线程池,你真的会用吗 在<深入源码分析Java线程池的实现原理>这篇文章中,我们介绍过了Java中线程池的常见用法以及基本原理. 在文中有这样一段描述: 可以通过Ex ...

最新文章

  1. 设计模式——门面(外观)模式
  2. There are no packages available for install
  3. 前谷歌资深架构师:真正搞懂spring底层到底有什么好处?
  4. python中如何调用类takes no arguments_关于python中的 take no arguments 的解决方法
  5. mysql 编码种类_MySQL 编码
  6. 110+优秀作品、20+热门领域曝光, 高通如何玩转“AI 的夏天”!
  7. 【论文写作】SSM超市管理系统如何写可行性分析
  8. A Hierarchical Deep Temporal Model for Group Activity Recognition
  9. SpringMvc之参数绑定注解详解之一
  10. OpenCV-图像处理(01、图像的加载、修改与保存)
  11. 【精选】OCR精选10个问题 百度paddleocr
  12. 摄像机标定的简单理解与纪要
  13. javaScript、PHP连接外卖小票机打印机方案(调用佳博、芯烨等)
  14. java 读文件时,readline的问题
  15. Windows Server 2022 英文版、简体中文版下载 (updated Dec 2021)(2022 年 1 月发布)
  16. 三极管发射极负反馈电阻的原理是什么?为什么就能起到负反馈作用呢
  17. 【开发技术】2万字分析shiro、spring security两大安全框架,spring session,OAuth2 入门级教程
  18. 【烈日炎炎战后端】Linux(0.3万字)
  19. 基于SpringBoot+Vue的在线音乐网站 音乐管理系统(源码调试+文档)
  20. 图片太大了怎么改小KB?教你2招无损图片压缩

热门文章

  1. 会议报到和撒离时间算会期_【NKMUN2021】城市会议新模式:让心动成为可能
  2. pcb板可挖孔吗_PCB板微孔加工方法之机械钻孔
  3. android 语音阅读软件,语音阅读器APP
  4. Android编译自定义sdk,java – 使用自定义android.bluetooth.而不是在android studio中默认的sdk android.jar中存在一个...
  5. 拔刀剑服务器r87修复版,我的世界拔刀剑mod刀剑修复教程
  6. 2019.01.21 bzoj2441: [中山市选2011]小W的问题(树状数组+权值线段树)
  7. Core DES加、解密
  8. IT创业公司如何选型,以避免未来出现的版权之争?
  9. 如何使用Git上传项目代码到github
  10. 深度学习——张量概念的引入