【高并发】线程的执行顺序没你想的那么简单

简介: 调用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

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

如何确保线程的执行顺序

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

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

可以使用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()方法能够保证线程的先后执行顺序。

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

目录
相关文章
|
1月前
|
人工智能 JSON 前端开发
【Spring boot实战】Springboot+对话ai模型整体框架+高并发线程机制处理优化+提示词工程效果展示(按照框架自己修改可对接市面上百分之99的模型)
【Spring boot实战】Springboot+对话ai模型整体框架+高并发线程机制处理优化+提示词工程效果展示(按照框架自己修改可对接市面上百分之99的模型)
|
4月前
|
Java 调度
【多线程和高并发】一:线程实现和synchronize
【多线程和高并发】一:线程实现和synchronize
|
6月前
|
数据采集 并行计算 Java
【文末送书】Python高并发编程:探索异步IO和多线程并发
【文末送书】Python高并发编程:探索异步IO和多线程并发
127 0
|
3月前
|
消息中间件 前端开发 JavaScript
JavaScript 线程:处理高并发任务的必备知识(下)
JavaScript 线程:处理高并发任务的必备知识(下)
JavaScript 线程:处理高并发任务的必备知识(下)
|
3月前
|
前端开发 JavaScript UED
JavaScript 线程:处理高并发任务的必备知识(上)
JavaScript 线程:处理高并发任务的必备知识(上)
JavaScript 线程:处理高并发任务的必备知识(上)
|
4月前
|
安全 Java 测试技术
高并发、多线程、分布式都不懂,你拿什么跳槽阿里、腾讯、京东?
Java多线程与高并发实战实践 先来看看高并发多线程一些大厂并发面试题,看你能答出几道!
|
4月前
|
消息中间件 缓存 安全
清华架构大牛剖析高并发与多线程的关系、区别,带你击穿面试难题
当提起这两个词的时候,是不是很多人都认为高并发=多线程? 当面试官问到高并发系统可以采用哪些手段来解决,是不是一脸懵逼?
|
6月前
|
druid 关系型数据库 MySQL
高并发下 MySQL Statement Cancellation Timer 的线程数暴涨
高并发下 MySQL Statement Cancellation Timer 的线程数暴涨
|
7月前
|
安全 Java 测试技术
高并发、多线程、分布式都不懂,你拿什么跳槽阿里、腾讯、京东?
Java多线程与高并发实战实践 先来看看高并发多线程一些大厂并发面试题,看你能答出几道! (1)synchronized的CPU原语级别是如何实现的? (2)无锁、偏向锁、轻量级锁、重量级锁有什么差别,升级过程如何? (3)线程间通信,同机器进程间通信,跨机器进程间通信,各有什么方法? (4)下列三种业务,应该如何使用线程池:
|
7月前
线程执行顺序怎么控制?
线程执行顺序怎么控制?
29 0

热门文章

最新文章