「JavaEE」线程状态

news/2024/5/28 4:02:52/ 标签: java-ee, java, 开发语言

🎇个人主页:Ice_Sugar_7
🎇所属专栏:JavaEE
🎇欢迎点赞收藏加关注哦!

线程状态

  • 🍉start 和 run 的区别
  • 🍉终止线程
  • 🍉join & 阻塞状态
  • 🍉线程六大状态

🍉start 和 run 的区别

这是一个经典的面试题,以下面代码为例:

java">public class MyThread extends Thread{@Overridepublic void run() {System.out.println("hello");}public static void main(String[] args) {Thread t = new MyThread();t.start();t.run();}
}

可以看到结果都输出“hello”
在这里插入图片描述
这两者的区别在于:
调用start创建一个新的线程,由这个线程执行打印 hello 的任务;
t.run() 则是调用 Thread 实例中的 run 方法,这个操作是在 main 主线程中打印 hello

如果我们把代码改成下面这样:在 run 方法和 main 方法中写个死循环,此时 t.run() 就只打印 hello thread,主线程没办法再向下执行

java">public class MyThread extends Thread{@Overridepublic void run() {while(true) {System.out.println("hello thread");}}public static void main(String[] args) {Thread t = new MyThread();t.run();while(true) {System.out.println("hello main");}}
}

🍉终止线程

一个线程,它的 run 方法如果执行完毕,那么它就终止了
如果我们想让线程提前终止,那就需要让 run 方法能够提前结束。我们一般会引入标志位,在其他进程中修改标志位的值来结束进程
也就是说:线程 A 什么时候结束,取决于另一个线程 B 什么时候修改 A 的标志位的值

Thread 实例提供的 currentThread 方法可以用来获取当前线程实例。也就是说哪个线程调用这个方法,得到的就是哪个线程的实例(类似 this)
比如下面这个代码,我们先看 while 循环

java">while(!Thread.currentThread().isInterrupted()) {     //...
}

isInterrupted 方法是用来查看当前线程是否被中断。如果一个线程被中断,那么得到的结果就为 true,它其实就相当于标志位
通过实例.interrupt() 可以中断线程

java">public class MyThread{public static void main(String[] args) throws InterruptedException{Thread t = new Thread(()-> {while(!Thread.currentThread().isInterrupted()) {System.out.println("线程运行中");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("线程执行完毕");});t.start();Thread.sleep(3000);//让 t 线程结束t.interrupt();}
}

看下结果:
在这里插入图片描述
可以看到代码出现异常之后,t 线程还在打印,这说明它并没有真正结束
而如果删掉匿名内部类中的 sleep,那么 interrupt 可以让线程顺利结束:

java">public static void main(String[] args) throws InterruptedException{Thread t = new Thread(()-> {while(!Thread.currentThread().isInterrupted()) {System.out.println("线程运行中");}System.out.println("线程执行完毕");});t.start();Thread.sleep(2000);t.interrupt();
}

在这里插入图片描述

那就说明 sleep 导致结果和预期结果不同
在执行 sleep 的过程中,调用 interrupt,可能会导致 sleep 的休眠时间还没到,就被提前唤醒
被提前唤醒后,会做两件事:
①抛出 InterruptedException (这个异常紧接着就会被 catch 捕获到)
②清除 Thread 对象的 isInterrupted 标志位

在上面的代码中,我们已经通过 Interrupt 方法把标志位设为 true 了,但是 sleep 被提前唤醒后就把标志位设回 false,所以导致循环继续执行
如果想让线程结束,只需在 catch 中加上 break 就 ok 了:

java">while(!Thread.currentThread().isInterrupted()) {System.out.println("线程运行中");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
}

当然,其实不止 sleep 有清空标志位的机制,很多方法都会这样

清空标志位是为了给我们更多的操作空间
比如上一行代码写的是 sleep(1000),但是现在1000ms 还没到就要终止线程,这样就前后矛盾了,此时就需要抛出异常,然后对这种情况进行具体的处理
我们可以在 catch 语句中加入一些代码来做处理:

  1. 让线程立即结束:加上 break
  2. 让线程继续执行:不加 break
  3. 让线程执行一些逻辑之后再结束:写一些其他代码,再 break

idea 生成的 catch 语句里面自动给的代码是 e.printStackTrace(),这个是在打印调用栈,或者是抛出另外一个异常。实际开发中这两种代码只是纯纯占个位置而已,没啥卵用


🍉join & 阻塞状态

虽然多个线程之间的执行顺序是不确定的,但是我们可以在应用程序中通过一些 api 来影响线程执行的顺序

join 就是一种方式,也是线程最核心的 api 之一,它通过影响线程结束的先后顺序来影响总的执行顺序
比如让 main 线程等待 t 线程,那就在 main 线程中调用 t.join()
执行 join 的时候,会看 t 线程是否正在运行
如果 t 正在运行,那么 main 线程就会阻塞(暂时不参与 CPU 执行)
如果 t 运行结束,那么 main 就会从阻塞中恢复过来,继续向下执行

由此可以看出:阻塞使这两个线程的结束时间产生先后顺序

在上面的例子中,就一定是 t 先结束,然后才是 main 结束
实际开发中一般不止 t 和 main 这两个线程,t 线程虽然可能是和其他线程共同进行调度的,但由于主线程一直处于等待状态,所以即使 t 中间经历多次 CPU 的切换,最终也能顺利执行完毕

join 的一个典型应用就是使用多个线程并发进行一系列计算,让一个线程阻塞等待上述计算线程,等到所有线程都计算完了,再让这个线程汇总结果
举个例子,弄两个线程,合作计算 1 到 100w 的和(一个线程计算 50w 个数),最后在 main 线程中打印结果:

java">public class MyThread{public static long sum = 0;public static void main(String[] args) throws InterruptedException{Thread t1 = new Thread(()-> {for(long i = 1;i <= 50_0000L;i++) sum += i;});Thread t2 = new Thread(()-> {for(long i = 50_0001L;i <= 100_0000L;i++) sum += i;});t1.start();t2.start();t1.join();t2.join();System.out.println(sum);}
}

在这里插入图片描述
在这里插入图片描述
不过这个结果貌似不太对,因为两次算出来的 sum 不一样,这就涉及到后面要讲的线程安全问题,不过这是后话,现在只需知道弄多个线程分别运算的效率,会比单独一个线程运算的效率高就 ok 了

然后我们在调用 join 的时候,可以看到它其实有三个重载的方法

在这里插入图片描述
millis 和 nanos 分别是毫秒和纳秒,不过因为系统的时间没法精确到纳秒级别,所以没啥卵用

如果参数不填时间(也就是第一个重载方法),那称为“死等”,就是说某个线程一定要等到另一个线程执行完才会继续向下执行。但是这种逻辑其实是不科学的,因为如果代码中因为死等导致程序卡住了,那就无法处理后续的逻辑,这就是非常严重的 bug 了
如果参数填了时间,那则是带有超时时间的等待,如果等待的时间超过超时时间,那就不会再等了,继续执行


🍉线程六大状态

Java 中线程的状态可分为:

  1. NEW:已经创建好了 Thread 对象,但是还没有调用 start 方法在系统中创建线程(只有处于 NEW 状态才能 start,并且一个 Thread 对象只能 start 一次)
  2. TERMINATED:系统内部的线程执行完毕
  3. RUNNABLE:就绪状态,表示这个线程正在 CPU 上执行,或者随时都可以去 CPU 上执行
  4. TIMED_WAITING:指定时间的阻塞,到达一定时间之后会自动解除阻塞,使用 sleep 或 带有超时时间的 join 会进入这个状态
  5. WAITING:不带时间的阻塞(死等),必须满足一定条件才会解除阻塞,使用 join 或者 wait 会进入这个状态
  6. BLOCKED:由于锁竞争引起的阻塞(后面说到线程安全时会详细介绍)

可以用一幅图来表示这六个状态间的联系:
在这里插入图片描述

这些状态在我们调试多线程代码的 bug 时可以作为重要参考依据
比如我们常说“程序卡住了”,这就说明一些关键的线程出现阻塞,我们可以通过观察线程的状态分析出一些原因


http://www.ppmy.cn/news/1434578.html

相关文章

开源AI Agent框架的选择

在选择开源AI Agent框架时,首先需要考虑的是框架的开发优先级、功能特性以及社区支持情况。SuperAGI是一个开发者优先的开源自主AI代理框架,它使开发人员能够快速可靠地构建、管理和运行有用的代理12。这表明SuperAGI可能是一个适合需要快速迭代和高度自定义能力的项目的良好…

Springboot+Vue项目-基于Java+MySQL的图书馆管理系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

低视力者出行升级:适配服务助力双手解放与环境感知

作为一名资深记者&#xff0c;我有幸深入了解并记录低视力者在日常出行中所面临的挑战与解决方案。近年来&#xff0c;低视力者辅助设备适配服务提供领域的创新成果&#xff0c;尤其是结合手机应用的辅助设备&#xff0c;正在以人性化、智能化的方式&#xff0c;帮助低视力者实…

在PostgreSQL中,如何创建一个触发器并在特定事件发生时执行自定义操作?

文章目录 解决方案示例代码1. 创建自定义函数2. 创建触发器 解释 在PostgreSQL中&#xff0c;触发器&#xff08;trigger&#xff09;是一种数据库对象&#xff0c;它能在特定的事件&#xff08;如INSERT、UPDATE或DELETE&#xff09;发生时自动执行一系列的操作。这些操作可以…

软考高级架构师:AI 通俗讲解软件维护的类型:正确性维护、适应性维护、完善性维护、预防性维护

软件维护是指在软件交付使用后进行的一系列活动&#xff0c;其目的是修复错误、提升性能或更新软件以适应变化的需求。通常&#xff0c;软件维护可以分为四种类型&#xff1a;正确性维护、适应性维护、完善性维护和预防性维护。下面我将用简单的例子和通俗的语言来解释这四种类…

linux复习提纲

后台运行 &&#xff1a;命令行尾输入&#xff0c;命令执行后台运行fg % 作业号&#xff1a;作业前台执行bg % 作业号&#xff1a;作业后台执行 用户权限 主user&#xff08;u&#xff09;-同一组的&#xff08;g&#xff09;-其他&#xff08;o&#xff09; rwx&#xf…

算法 第44天 动态规划6

518 零钱对换II 给你一个整数数组 coins 表示不同面额的硬币&#xff0c;另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额&#xff0c;返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 …

【PHP快速上手(十四)】

目录 PHP快速上手&#xff08;十四&#xff09;PHP 中常用数据库操作使用 WHERE 子句进行条件查询使用 ORDER BY 子句进行排序使用 UPDATE 语句更新数据使用 DELETE 语句删除数据执行事务总结 PHP快速上手&#xff08;十四&#xff09; PHP 中常用数据库操作 当使用 PHP 中的…

2024/4/19学习笔记 vector模拟实现(2)

本次学习重点 1.迭代器区间构造和size_t n 构造 2.string扩容问题 3.erase的缺陷 1.迭代器区间构造和size_t n 构造 vector支持用一段迭代器区间构造&#xff0c;也可以支持任意类型的迭代器区间&#xff0c;所以要写成函数模板 template <class InputIterator> ve…

VUE-配置-流程

VUE-配置-流程 ---1---.安装 NVM: ---2---.用NVM:安装node.js &#xff1a; 修改源&#xff1a; nvm root 命令&#xff0c;可以查看nvm的安装根路径在那个文件夹 root地址&#xff0c;找到setting.txt文件并打开 复制粘贴以下代码&#xff0c; 保存完成nvm源修改&#xff…

MATLAB初学者入门(8)—— 动态规划

动态规划是一种数学方法&#xff0c;用于解决具有递归结构的决策问题&#xff0c;特别是那些涉及顺序决策的问题。在MATLAB中实现动态规划&#xff0c;可以通过定义状态变量、决策变量、状态转移方程以及目标函数来完成。以下是具体的案例分析。 案例分析&#xff1a;项目资源…

Oracle使用内部包自定义创建表空间和用户

如果之前有类似的表空间,可以使用dbms自动生成对应的表空间和数据文件 select dbms_metadata.get_ddl(TABLESPACE,ts.tablespace_name) from dba_tablespaces ts; 可以使用类似的 SQL> set echo off SQL> spool /data/logs/create_tablespace.log SQL> select dbms…

SqL--DCL数据控制语言

文章目录 数据控制语言用户角色 赋权收权删除用户自定义角色 数据控制语言 用户 用户&#xff1a;用来登录数据库的账号 需要有权限的用户或者管理员用户system 创建用户&#xff1a; 语法&#xff1a; CREATE USER 用户名 IDENTIFIED BY 密码;注意&#xff1a;1.此时的用户…

浅析Java中的LinkedList和ArrayList特点和底层

本期经验 LinkedList适合于删除和插入元素的操作&#xff0c;对首元素和尾元素的删除和修改插入极好&#xff0c;ArrayList适合于元素的修改和查询。 LinkedList LinkedList的底层使用双向链表来写&#xff0c;这导致其每次查询和修改元素都必须从首元素开始以此往下找&…

.NET 基于Socket中转WebSocket

前言 针对IOS App Proxy Server无法直连WebSocket&#xff0c;建立 Socket中转端。 WebSocket 端&#xff1a; WebSocket 端用于实现实时通信功能。 WebSocket 端通过 WebSocket 协议与中转端通信&#xff0c;中转端可以通过 WebSocket 或其他传输协议与 WebSocket 端建立连…

Pytorch或Tensorflow 深度学习库安装 (简易版)

Tensorflow 2.X安装 0、 pytorch 支持 conda虚拟环境 cuda 和 cudnn1、创建conda环境2、测试GPU是否可用3、在机器上安装cuda 和 cudnnCUDA 安装cudnn 安装 0、 pytorch 支持 conda虚拟环境 cuda 和 cudnn 如果只用pytorch&#xff0c; 只需在虚拟环境安装cuda 和 cudnn即可&am…

JavaScript 模块导出示例

JavaScript 模块导出示例说明 在 JavaScript 中&#xff0c;我们可以通过 export 关键字将模块中的功能导出&#xff0c;以供其他模块使用。导出可以是单个默认值&#xff0c;也可以是多个命名值。本文将分别介绍导出单个值和导出多个值的示例说明。 导出单个值 当模块中只有…

MySQL你想知道序列当前生成的值,你可以使用SHOW TABLE STATUS命令或者查询information_schema数据库

在MySQL中&#xff0c;如果你想知道序列当前生成的值&#xff08;例如&#xff0c;自增主键的当前值&#xff09;&#xff0c;你可以使用SHOW TABLE STATUS命令或者查询information_schema数据库。 使用SHOW TABLE STATUS命令&#xff1a; 这个命令可以显示关于表的各种信息&…

AI助力科研创新与效率双提升:ChatGPT深度科研应用、数据分析及机器学习、AI绘图与高效论文撰写

2022年11月30日&#xff0c;可能将成为一个改变人类历史的日子——美国人工智能开发机构OpenAI推出了聊天机器人ChatGPT3.5&#xff0c;将人工智能的发展推向了一个新的高度。2023年4月&#xff0c;更强版本的ChatGPT4.0上线&#xff0c;文本、语音、图像等多模态交互方式使其在…

MySQL数据库精讲001——概述

MySQL数据库精讲001——概述 文章目录 MySQL数据库精讲001——概述1.1 安装1.1.1 版本1.1.2 安装一、下载二、解压三、配置1. 添加环境变量2. 初始化MySQL3. 注册MySQL服务4. 启动MySQL服务5. 修改默认账户密码 四、登录MySQL五、卸载MySQL 1.1.3 连接1.1.4 企业使用方式(了解)…