大家好,我是冰河~~
今天,我们一起来讲讲线程的执行顺序,可能与你想象的不一样,全程高能,一起加油~~
一、线程的执行顺序是不确定的调用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()方法会继续向下执行,启动第二个子线程,并执行子线程的业务逻辑,以此类推。
好了,今天就到这儿吧,我是冰河,我们下期见~~
聪明的人类发现把简单的开关组合起来可以表达复杂的bool逻辑,在此基础之上构建...
趋势 1. 云原生发展趋势 云原生(Cloud Native)是最近几年非常火爆的话题,在 202...
云已成趋势,未来相信会有越来越多的IT服务,直接在云上创建、部署和优化。这样...
本文转载自微信公众号「Java中文社群」,作者磊哥 。转载本文请联系Java中文社群...
据外媒最新报道,谷歌和亚马逊网站已经改变了他们的想法,将他们的一些通用顶级 ...
SSH 密钥登录与密码登录有何区别? SSH 密钥是一种远程登录 Linux 服务器的方式...
.site可以备案吗? .site域名 是可以进行备案的。. site域名 已经于2016年12月通...
创作人:骆潇龙 Elasticsearch 本着让用户方便快捷的使用搜索功能的原则,对数据...
落户草原云谷,挺进世界屋脊,伴随今年政府工作报告给新基建吹来新风,我国大数...
哪里的 域名注册 便宜? 域名 注册的价格,除了与注册的域名后缀有关,还与选择...