大家好,我是冰河~~

最近常常有读者问我:冰河,线程到底是依照怎么的程序执行的呀?为了同一答复大家的这个问题,明天我就独自写一篇文章吧。好了,不多说了,进入明天的正题。

一、线程的执行程序是不确定的

调用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办法,后果如下所示。

thread1thread2thread3

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

thread1thread3thread2

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

thread2thread3thread1

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

二、如何确保线程的执行程序

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();    }}

能够看到,ThreadSot02类比ThreadSort01类,在每个线程的启动办法上面增加了调用线程的join()办法。此时,运行ThreadSort02类,后果如下所示。

thread1thread2thread3

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

thread1thread2thread3

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

thread1thread2thread3

能够看到,每次运行的后果都是雷同的,所以,应用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()办法会持续向下执行,启动第二个子线程,并执行子线程的业务逻辑,以此类推。