Java中线程顺序执行解析

news/2024/7/7 19:37:05 标签: java, 前端, 大数据

这篇文章给大家介绍Java中线程顺序执行解析,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

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

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

package io.ck.concurrent.lab03;

/**
 * @author ck
 * @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.ck.concurrent.lab03;
/**
 * @author ck
 * @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中线程顺序执行解析就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。


http://www.niftyadmin.cn/n/860593.html

相关文章

使用Nginx做RTMP直播服务器

今天来简单记录一下nginx来配置一个RTMP服务器的过程&#xff0c;实际特别简单&#xff0c;我们使用的是集成好的解压版软件&#xff0c;下载地址如下&#xff1a; https://download.csdn.net/download/lp840312696/11829426 解压出来将文件夹放到D盘根目录下 双击nginx.exe即…

Spring @Valid @Validated实现验证的方法

小编给大家分享一下Spring Valid Validated实现验证的方法&#xff0c;相信大部分人都还不怎么了解&#xff0c;因此分享这篇文章给大家参考一下&#xff0c;希望大家阅读完这篇文章后大有收获&#xff0c;下面让我们一起去了解一下吧&#xff01; 对于属性的验证有如下一些注…

Netty实战学习笔记01——简单Echo服务器编写

十一假期以过去一大半&#xff0c;本来是计划每天一篇学习记录进行更新的&#xff0c;结果今天才开始写第一篇博文。这就是为什么我还是一个菜鸟的原因。既然不能每天进步一点&#xff0c;那么就每周进步一点&#xff01; 最近博主新入手一本《Netty实战》&#xff0c;从今天开…

java实现递归菜单树

本文实例为大家分享了java实现递归菜单树的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下 1.表结构 SET FOREIGN_KEY_CHECKS0;-- ---------------------------- -- Table structure for menu -- ---------------------------- DROP TABLE IF EXISTS menu; CREATE …

Netty实战学习笔记02——简单Echo客户端编写

今天来继续记录Echo的下半部分&#xff0c;Echo客户端的编写。 这里我们需要简单介绍一下我们所需要做的内容&#xff1a; 为初始出啊客户端&#xff0c;创建一个Bootstarp实例&#xff1b;为进行事件处理分配一个NIOEventLoopGroup实例&#xff0c;其中事件处理包括创建新的…

Git怎么配置别名

这篇文章主要介绍“Git怎么配置别名”&#xff0c;在日常操作中&#xff0c;相信很多人在Git怎么配置别名问题上存在疑惑&#xff0c;小编查阅了各式资料&#xff0c;整理出简单好用的操作方法&#xff0c;希望对大家解答”Git怎么配置别名”的疑惑有所帮助&#xff01;接下来&…

Java基础学习03——通过继承Comparable方法实现排序

今天来记录一下一个知识点&#xff0c;学了这个功能点之后就可轻松的将对象进行排序了。 只需要实现Comparable接口即可通过Collections.sort()方法进行排序了。 示例代码如下&#xff1a; package com.youyou.util;import lombok.Data;import java.util.ArrayList; import …

怎么使用Git远程删除某个历史提交记录

这篇文章主要介绍“怎么使用Git远程删除某个历史提交记录”的相关知识&#xff0c;小编通过实际案例向大家展示操作过程&#xff0c;操作方法简单快捷&#xff0c;实用性强&#xff0c;希望这篇“怎么使用Git远程删除某个历史提交记录”文章能帮助大家解决问题。 引言 在开发中…