乐趣区

关于多线程:关于线程的执行顺序可能真的只是你以为的你以为

摘要: 明天,咱们就一起来看看线程到底是如何执行的,它的程序又是怎么的?

本文分享自华为云社区《线程的执行程序与你想的不一样!!》,作者:冰 河。

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

调用 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() 办法会持续向下执行,启动第二个子线程,并执行子线程的业务逻辑,以此类推。

点击关注,第一工夫理解华为云陈腐技术~

退出移动版