JavaEE 初阶篇-深入了解多线程等待与多线程状态

news/2024/5/14 13:48:35

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍

文章目录

        1.0 线程等待

        1.1 线程等待 - join() 方法

        1.1.1 main 线程中等待多个线程

        1.1.2 main 线程等待 t2 线程且t2 线程等待 t1 线程

        1.1.3 其他线程阻塞等待 main 线程

        1.1.4 在指定的时间内阻塞等待

        1.2 线程等待 - Thread.sleep() 方法

        2.0 线程状态

        2.1 新建状态 - NEW

        2.2 就绪状态 - Runnable

        2.3 终止状态 -Terminated

        2.4 等待状态 - Waiting

        2.5 超时等待状态 - Time_Waiting

        2.6 阻塞状态 - Blocked

        2.7 线程状态之间的相互转换图


        1.0 线程等待

        在线程编程中,线程等待是指一个线程暂停执行,直到某个条件满足或者其他线程执行完毕后再继续执行。线程等待的方法:join() 方法Thread.sleep() 方法等等

        1.1 线程等待 - join() 方法

       join() 方法是 Thread 类的一个方法,用于让一个线程等待另一个线程执行完毕。当在一个线程对象上调用 join() 方法时,当前线程会被阻塞,直到被调用的线程执行完毕。

        具体来说,调用 thread.join() 会使当前线程等待 thread 线程执行完毕。如果 thread 线程已经执行完毕,那么 join() 方法会立即返回;如果 thread 线程还在执行,当前线程会被阻塞,直到 thread 线程执行完毕。

代码如下:

public class demo1 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{for (int i = 0; i < 1000; i++) {System.out.println("正在执行 thread 线程");}});thread.start();thread.join();System.out.println("执行 main 线程");}
}

        main 线程调用 thread.join() ,就会阻塞 main 线程继续执行,会让 thread 线程执行完毕之后,main 线程解除阻塞,继续执行下去。

运行结果:

补充: main 中调用 join() 方法,有以下可能性:

        1.0 如果 t 线程此时已经结束了,此时 join() 方法就会立即返回。

        2.0 如果 t 线程此时还没有结束,此时 main 就会阻塞等待,一直等待到 t 线程结束之后,main 线程才会接触阻塞,继续执行。

        3.0 如果调用等待阻塞的线程对象还没创建 pcb 的时候(即还没 start() 的时候),那么调用 join() 方法的线程会直接解除阻塞。

        1.1.1 main 线程中等待多个线程

        在 main 线程中多次调用 join() 方法时,先执行 t1.join(),如果 t1 还没结束,main 继续阻塞等待,t1 结束之后,继续执行 t2.join() 方法,再等待 t2 结束。注意,t1 与 t2 之间同样是抢占式执行随机调度,所以先后顺序对于 main 线程来说没有什么区别。

代码如下:

public class demo2 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{for (int i = 0; i < 5; i++) {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("正在执行 t1 线程");}});Thread t2 = new Thread(()->{for (int i = 0; i < 5; i++) {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("正在执行 t2 线程");}});t1.start();t2.start();t1.join();t2.join();System.out.println("正在执行 main 线程");}
}

运行结果:

        1.1.2 main 线程等待 t2 线程且t2 线程等待 t1 线程

        这种情况是按顺序执行的,大致就是串行执行一样。顺序为:先要执行完 t1 再执行 t2 最后再执行 main 线程。

代码如下:

public class demo3 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{for (int i = 0; i < 5; i++) {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("正在执行 t1 线程");}});Thread t2 = new Thread(()->{try {t1.join();} catch (InterruptedException e) {throw new RuntimeException(e);}for (int i = 0; i < 5; i++) {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("正在执行 t2 线程");}});t1.start();t2.start();t2.join();System.out.println("执行 main 线程");}
}

        t1.start() 立即创建 t1 线程,再 t2.start() 创建线程,t1 没有任何阻塞就会直接执行代码,而 t2 遇到了阻塞,需要等待 t1 执行完毕之后,t2 才会解除阻塞,同时由于 main 线程阻塞了,需要等待 t2 执行完毕,当 t2 执行完毕之后,main 线程解除阻塞了,执行 main 线程中的代码。

运行结果:

        1.1.3 其他线程阻塞等待 main 线程

        t1 线程阻塞等待 main 线程执行完毕之后,再执行 t1 线程。

代码如下:

public class demo4 {public static void main(String[] args) throws InterruptedException {//拿到当前 main 线程对象Thread mainThread = Thread.currentThread();Thread t1 = new Thread(()->{//在 t1 线程中调用 join() 方法,//阻塞当前 t1 线程,等待 main 线程执行完毕try {mainThread.join();} catch (InterruptedException e) {throw new RuntimeException(e);}for (int i = 0; i < 5; i++) {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("正在执行 t1 线程");}});t1.start();for (int i = 0; i < 5; i++) {Thread.sleep(1000);System.out.println("正在执行 main 线程");}}
}

运行结果:

        1.1.4 在指定的时间内阻塞等待

        join() 方法还有一个重载的版本,可以指定一个超时时间,即 join(long millis),表示当前线程最多等待 millis 毫秒,如果超过这个时间 thread 线程还没有执行完毕,当前线程会继续往下执行。简单来说,即使 thread 这个线程还没有结束,main 线程都不会继续等待了;如果 thread 在规定的时间内提前结束,那么 main 也会提前解除阻塞。

代码如下:

public class demo5 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{for (int i = 0; i < 1000; i++) {System.out.println("正在执行 thread 线程");}});thread.start();//等待 0.1 s 就解除阻塞 main 线程,即不会继续等待了thread.join(1000);System.out.println("正在执行 main");}
}

运行结果:

        1.2 线程等待 - Thread.sleep() 方法

        是一个静态方法,它使当前线程暂停执行一段时间。该方法接受一个以毫秒为单位的时间参数,指定线程暂停的时间长度。在这段时间内,线程不会执行任何操作,但是线程的状态仍然是 Runnable 状态,可以随时被调度器调度执行。

        需要注意的是,Thread.sleep() 方法不是真正意义上的线程等待,它只是让线程暂停执行一段时间,不会释放锁或资源。在实际开发中,应根据具体需求选择合适的线程等待机制,以确保程序的正确性和效率。

代码如下:

public class demo6 {public static void main(String[] args) {Thread thread = new Thread(()->{for (int i = 0; i < 5; i++) {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("正在执行 thread 线程");}});thread.start();}
}

运行结果:

        每相隔 1 秒就会输出一次。

        需要注意的是,这里的 Thread.sleep() 方法是受查异常,且 run() 是重写父类的方法,该 run() 方法的方法名、参数列表、声明异常都需要与父类保持一致,因此这里不能声明异常,只能捕获异常处理。

        2.0 线程状态

        在 Java 中主要包括六种不同的状态。

        2.1 新建状态 - NEW

        当创建一个线程对象时,线程处于新建状态,此时线程对象已经创建好了,但是还没调用 start() 方法启动线程,因此线程还没被创建出来

        2.2 就绪状态 - Runnable

        有两种情况都属于就绪状态:1)还没运行,就绪状态。但是线程已经准备好运行了,只等待被 CPU 调度执行。2)线程正在被 CPU 调度执行中,运行状态。总而言之,无论是就绪状态还是运行状态在 Java 中都属于 Runnable 状态,即就绪状态。

        2.3 终止状态 -Terminated

        线程执行完任务后或者出现异常导致线程终止时,线程进入终止状态。在终止状态下,线程不再执行任务。

        2.4 等待状态 - Waiting

        线程进入等待状态通常时因为调用了 thread.join() 方法等等。

代码如下:

public class demo7 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{while (true){System.out.println(1);}});thread.start();thread.join();System.out.println("正在执行 main 线程");}
}

演示线程等待:

        main 线程调用了 thread.join() 方法阻塞等待 thread 线程,又因为 thread 还当前为止还没结束, 所以当前 main 线程被阻塞了,因此 main 状态为 Waiting 状态。对于 thread 线程来说,目前的状态为 Runnable 状态。

        2.5 超时等待状态 - Time_Waiting

        线程调用带有超时参数的等待方法,比如 Thread.sleep(long millis) 等待方法。线程会进入超时等待状态下,线程会等待一段时间后自动恢复到就绪状态。

代码如下:

public class demo8 {public static void main(String[] args) {Thread thread = new Thread(()->{try {Thread.sleep(9000000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("正在执行 thread 线程");});thread.start();System.out.println("正在执行 main 线程");}
}

演示超时等待:

        2.6 阻塞状态 - Blocked

        线程在特定情况下,会进入阻塞状态,比如调用了 Thread.sleep() 方法或者加锁。在线程阻塞状态下,线程暂时停止执行,直到满足特定条件后,才能继续执行。

代码如下:

死锁状态:两个线程两把锁

public class demo10 {public static void main(String[] args) {Object o1 = new Object();Object o2 = new Object();Thread t1 = new Thread(()->{synchronized (o1){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (o2){System.out.println("正在执行 t1 线程");}}});Thread t2 = new Thread(()->{synchronized (o2){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (o1){System.out.println("正在执行 t2 线程");}}});t1.start();t2.start();}
}

演示阻塞状态:

        2.7 线程状态之间的相互转换图


http://www.mrgr.cn/p/34666406

相关文章

SpringBoot项目接入Nacos的实现步骤

前言 项目中没有使用nacos官方提供的方式使用SpringBoot的集成方式来进行集成,而是使用了Alibaba Spring Cloud的依赖包进行集成。 原因是因为官网提供的SpringBoot集成方式中,同时使用配置中心和服务发现功能,会使得服务发现功能配置的部分属性冲突不生效。最直接的就是配置…

移动平台实时动态多点光源方案:Cluster Light

一、什么是 Cluster Light&#xff0c;它具体如何实现多点光源效果&#xff1f; 对于移动设备&#xff0c;如何支持场景中大量的实时点光源一直以来都是比较棘手的问题&#xff0c;因此对于过去&#xff0c;往往有如下两种常规方案&#xff1a; 静态点光源直接烘焙&#xff0…

UE4 C++ Widget的NativeConstruct 与 NativePreConstruct

构造函数 由于Widget是由UE的反射系统创建的,其生命周期由UE引擎管理,所以并不存在构造函数,UE为Widget类定义了两个虚函数NativeConstruct 与 NativePreConstruct来充当构造函数的作用。而这两个函数的调用都必须在Widget被实例化之后才能进行调用 如何在Widget中获取角色 …

【AIGC】如何在Windows/Linux上部署stable diffusion

文章目录 整体安装步骤windows10安装stable diffusion环境要求安装步骤注意事项参考博客其他事项安装显卡驱动安装cuda卸载cuda安装对应版本pytorch安装git上的python包Q&A linux安装stable diffusion安装anaconda安装cudagit 加速配置虚拟环境挂载oss&#xff08;optional…

P1484 种树 题解

P1484 种树有 \(n\) 个坑。第 \(i\) 个坑种树的价值是 \(c_i\),相邻坑不能同时种。可以种 \(k\) 颗树,求最大价值。模拟费用流,建图类似这样: 中间两层结点之间有 \(7\) 条边,表示 \(n=7\) 的情况。相邻两条边,例如 \(1,2\) 总流入量为 \(1\),\(2,3\) 总流出量为 \(1\),…

新穗青少年开展“小小公民科学家”科考活动探索生物多样性

为帮助“新穗”青少年了解本土文化&#xff0c;提升个人知识面和动手能力&#xff0c;贯彻落实《未成年人保护法》《家庭教育促进法》《广东省生态环境教育条例》等有关文件精神&#xff0c;3月24日&#xff0c;天河区绿日同学公益服务促进会联合华南农业大学农潮工作室、广州城…

Sql Server设置用户只能查看并访问特定数据库

1.新建登录用户以管理员身份登陆数据库(权限最高的身份如sa),点击安全性->登录名,右键新建登录名,输入登录名和密码,取消强制实施密码策略。 2.将服务器角色设置为public 3.将public服务器角色的属性->取消查看所有数据库的权限点击安全性->服务器角色->publ…

华为流量整形配置

组网需求 如图1所示&#xff0c;企业网内部LAN侧的语音、视频和数据业务通过Switch连接到RouterA的Eth2/0/0上&#xff0c;并通过RouterA的GE3/0/0连接到WAN侧网络。 不同业务的报文在LAN侧使用802.1p优先级进行标识&#xff0c;在RouterA上根据报文的802.1p优先级入队列&…

electron 开发

教程:https://www.electronjs.org/zh/docs/latest/tutorial/quick-start 系统:Windows 10 工具: PowerShell 文件目录:index.html main.js package.json 和 preload.js 是手动添加的,其余是编译生成的index.html<!DOCTYPE html> <html><head><meta c…

2024最新版克魔助手抓包教程(9) - 克魔助手 IOS 数据抓包

引言 在移动应用程序的开发中&#xff0c;了解应用程序的网络通信是至关重要的。数据抓包是一种很好的方法&#xff0c;可以让我们分析应用程序的网络请求和响应&#xff0c;了解应用程序的网络操作情况。克魔助手是一款非常强大的抓包工具&#xff0c;可以帮助我们在 Android …

vue3+ts白屏问题解决

文章目录 打开白屏解决方法可能出现问题使用base导致的使用baseUrl导致的 注意点vue3ts白屏问题知识分享 打开白屏 解决方法 在vue.config.js页面 添加publicPath:./, const { defineConfig } require(vue/cli-service)module.exports defineConfig({ transpileDependenci…

客快物流大数据项目(八十一): Kudu原理 有用 看1

​Kudu原理 一、表与schema Kudu设计是面向结构化存储的,因此Kudu的表需要用户在建表时定义它的Schema信息,这些Schema信息包含:列定义(含类型) Primary Key定义(用户指定的若干个列的有序组合)数据的唯一性,依赖于用户所提供的Primary Key中的Column组合的值的唯一性。…

Docker进阶:Docker Swarm(集群搭建) —实现容器编排的利器

Docker进阶&#xff1a;Docker Swarm&#xff08;集群搭建&#xff09; —实现容器编排的利器 1、什么是Docker Swarm&#xff1f;2、Docker Swarm 与 Docker Compose的区别3、创建一个Swarm集群&#xff08;1-Manager&#xff0c;2-Worker&#xff09;1、资源准备2、初始化Swa…

ICLR 2024 | FeatUp: A Model-Agnostic Framework for Features at Any Resolution

论文&#xff1a;https://arxiv.org/abs/2403.10516代码&#xff1a;https://github.com/mhamilton723/FeatUp 背景动机 深层特征是计算机视觉研究的基石&#xff0c;捕获图像语义并使社区即使在零或少样本情况下也能解决下游任务。然而&#xff0c;这些特征通常缺乏空间分辨率…

3.2324物理强基小记

物理强基课有人听强基课是听提高,有人听强基课是听水题,有人听强基课是听新课,怎么回事呢?弹簧 类SHMSHM 中,都可以规约成 \(E_p=\dfrac{1}{2}kx^2,E_k=\dfrac{1}{2}mv^2\)例1\(Q\) 固定,\(q\) 穿在绝缘光滑杆,总长 \(l\),一个小移动 \(x\),\(x<<l\)Trick:\((1…

URL编码:原理、应用与安全性

在网络世界中,URL(统一资源定位符)是我们访问网页、发送请求的重要方式。然而,URL 中包含的特殊字符、不安全字符以及保留字符可能会导致传输错误或安全风险。为了解决这些问题,URL 编码应运而生。本文将从概念介绍、编码规则、编码与解码、常见应用场景、历史演变、安全性…

KingbaseES V8R6集群运维案例之---级联备库upstream节点故障

KingbaseES V8R6集群运维案例之---级联备库upstream节点故障案例说明: 在KingbaseES V8R6集群,构建级联备库后,在其upstream的节点故障后,级联备库如何处理? 适用版本:KingbaseES V8R6 集群架构:案例一: 一、配置集群的recovery参数(all nodes) Tips: 关闭备库的aut…

让IIS支持.NET Web Api PUT和DELETE请求

前言 有很长一段时间没有使用过IIS来托管应用了&#xff0c;今天用IIS来托管一个比较老的.NET Fx4.6的项目。发布到线上后居然一直调用不同本地却一直是正常的&#xff0c;关键是POST和GET请求都是正常的&#xff0c;只有PUT和DELETE请求是有问题的。经过一番思考忽然想起来了I…

KingbaseES V8R6运维案例之---归档日志批量解析

案例说明: KingbaseES V8R6数据库的wal日志归档如果通过sys_rman工具执行,默认日志将归档在备份目录下,归档日志被压缩及日志文件名包含随机字符串,在执行sys_waldump时,必须解压缩和改名后才能被识别。 适用版本: KingbaseES V8R6 一、数据库归档及备份配置 1、归档配置…