【JavaEE】线程创建和终止,Thread类方法,变量捕获(7000字长文)
阿华代码,不是逆风,就是我疯,你们的点赞收藏是我前进最大的动力!!7000字长文,希望本文内容能够帮助到你!
目录
一:创建线程五种方式
方式一:继承Thread类,再实例化
方式二:实现Runnable接口,重写run方法
方式三:匿名内部类写法
方式四:Runnable+匿名内部类
方式五:lambda表达式
二:Thread常见方法
1:Thread方法
2:获取Thread属性的方法
(1)守护进程
(2).setDaemon()方法
①前台/后台线程
②.setDaemon
③结果分析:
(3)isAlive()
(4)start方法和run方法的区别
三:如何提前终止一个线程
1:标志位——isQuit
(1)变量捕获:重点重点重点
(2)lambda复制和传参理解
2:Thread内置变量isinterrupted
(1)isinterrupted本质
(2)sleep清空标志符
(3)解决方式(catch中加代码)
一:创建线程五种方式
方式一:继承Thread类,再实例化
package thread;/*** Created with IntelliJ IDEA.* Description:* User: Hua YY* Date: 2024-09-17* Time: 14:20*/
class MyThread extends Thread{@Overridepublic void run() {System.out.println("这就是进入该线程的入口");}
}
public class ThreadDemo1 {public static void main(String[] args) {//根据类,创建实例,线程实例才是真正的线程//一般用向上转型的写法Thread t = new MyThread();t.start();}
}
方式二:实现Runnable接口,重写run方法
package thread;/*** Created with IntelliJ IDEA.* Description:* User: Hua YY* Date: 2024-09-19* Time: 9:20*/
class MyTread3 implements Runnable{@Overridepublic void run() {while(true){System.out.println("这里是run线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
public class TreadDome3 {public static void main(String[] args) {Runnable runnable = new MyTread3();//相当于借刀杀人了Thread t = new Thread(runnable);t.start();while(true){System.out.println("这里是main函数线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}
注:可以通过Runnable这个接口,可以抽象出一段被其他实体执行的代码,还是需要搭配Thread类来进行使用
方式三:匿名内部类写法
在实例化Thread对象时{}里创建匿名内部类,重写run方法
匿名内部类:
①没有名字,不能重复使用
②这个新的类继承自Thread,并且{}里可以定义子类的属性和方法
③t的指向不仅仅是Thread,还有它的子类(虽然没名字),(即不仅是Thread的实例,也是其子类的实例)
package thread;/*** Created with IntelliJ IDEA.* Description:* User: Hua YY* Date: 2024-09-19* Time: 9:39*/ public class ThreadDome4 {public static void main(String[] args) {Thread thread = new Thread(){public void run(){while(true){System.out.println("这里是由匿名内部类实现的线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}};thread.start();while(true){System.out.println("这里是main函数入口");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}} }
方式四:Runnable+匿名内部类
实现Runnable接口,重写run方法实现匿名内部类
package thread;/*** Created with IntelliJ IDEA.* Description:* User: Hua YY* Date: 2024-09-19* Time: 10:07*/
public class ThreadDome5 {public static void main(String[] args) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {while(true){System.out.println("run method");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}});thread.start();while(true){System.out.println("main method");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}
方式五:lambda表达式
lambda表达式,适合简单直观的代码,如果代码过于复杂还是需要提取出来的
在Java中方法的实现依赖于类,方法不能脱离类单独存在,这里就导致为了设置回调函数,不得不套上一层类,但是并不常用——引出了lambda表达式。
函数式接口相当于在没有破坏java原有的规则上(方法不能脱离类单独存在),单独给lambda一个解释
第一个标记的红色方框中的()-> ,()括号中可以带参数
package thread;/*** Created with IntelliJ IDEA.* Description:* User: Hua YY* Date: 2024-09-19* Time: 10:29*/
public class ThreadDome55 {public static void main(String[] args) {Thread thread = new Thread(()->{while (true){System.out.println("run method");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread.start();while(true){System.out.println("main method");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}
二:Thread常见方法
1:Thread方法
2:获取Thread属性的方法
后面的代码中会运用到,有相应的解释
(1)守护进程
通过之前的学习,我们知道,main方法走完了,整个进程就结束了。 现在我们引入了线程的概念,那这个结论还准确吗。有没有例外呢?
下面我们举例:代码里面我们创建main函数线程
这里我们使用jconsole工具来辅助(上一篇博客有讲怎么使用jconsole前面)
package thread;/*** Created with IntelliJ IDEA.* Description:* User: Hua YY* Date: 2024-09-19* Time: 10:58*/
public class ThreadDome6 {public static void main(String[] args) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {while(true){System.out.println("run method");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}} , "这个线程的名字叫:测试");thread.start();}}
我们可以发现jconsole线程运行详细信息中,没有main函数,但是有我们的测试线程,很明显即使main函数这个前台线程已经结束了,但是这个进程依旧还在运行(可以说是这个“这个线程的名字叫:测试”的线程还在运行)
由此我们引出一组概念:前台线程和后台线程
(2).setDaemon()方法
①前台/后台线程
重点:咱们创建的线程,默认都是前台线程,会阻止进程的结束,只要前台线程没有执行完,进程就不会结束,即使main函数这个线程已经执行完毕
(这么理解:后台进程没有话语权,只要还有前台线程,进程就不会结束)
②.setDaemon
理解:Daemon是守护的意思,这个方法是把某线程设置为后台线程(守护线程)
注:这个方法一定要在“.start”方法之前设置
接上文,我们引入.setDaemon()方法,可以将Thread下的线程从前台改为后台,下面会详细解释两者区别。
③结果分析:
什么都没有打印是因为,我们把main函数设置为了后台线程,这个线程走完了,那整个进程都结束了,Thread这个前台线程
(3)isAlive()
isAlive(),方法,表示了当前内核中的线程(pcb)是否还存在,内核中的线程和实例的周期是不一致的
①在创建完Thread对象之后,start方法之前,pcb还没有创建出来,所以答疑结果为false
②在start方法之后,线程结束之前,对象存在,pcb存在,打印结果为true,
③线程run完了,pcb得到释放,为false
④注意下面的代码Thread.sleep(5000) ,如果沉睡时间改为3000ms以下,那么最后一条打印结果就不确定了,线程的随机调度
package thread;/*** Created with IntelliJ IDEA.* Description:* User: Hua YY* Date: 2024-09-19* Time: 18:24*/
public class ThreadDemon8 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}});System.out.println(thread.isAlive());//false,有对象,无pcb线程thread.start();//创建线程System.out.println(thread.isAlive());//true,有对象,有pcbThread.sleep(5000);//main函数线程沉睡5s,此时Thread线程3s沉睡完了,thread线程结束,thread对象还在System.out.println(thread.isAlive());//false}}
(4)start方法和run方法的区别
start方法核心是创建一个新的线程,但是run方法仅仅是一个方法、一个线程的入口,类似main方法的入口,两者不能说没有关系,简直是毫无关系!!!
package thread;/*** Created with IntelliJ IDEA.* Description:* User: Hua YY* Date: 2024-09-19* Time: 18:56*/
public class ThreadDemon9 {public static void main(String[] args) {Thread t1 = new Thread(){@Overridepublic void run() {while(true){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("run线程方法正在执行");}}};//t1.run();//不屏蔽这行代码,将会一直死循环在run方法中,单线程main的t1.start();System.out.println("main函数线程正在执行");}}
三:如何提前终止一个线程
1:标志位——isQuit
我们需要在Thread实例化中写符合能够控制线程的结构,下面的代码我们引入isQuit这个静态成员变量,来控制while循环,进而达到提前终止线程这样一个目的
/*** Created with IntelliJ IDEA.* Description:* User: Hua YY* Date: 2024-09-19* Time: 19:18*/
public class ThreadDemon12 {private static boolean isQuit = false;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{while(!isQuit){try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("t1线程正在运行");}});t1.start();Thread.sleep(2001);isQuit = true;System.out.println("终止t1线程");}}
(1)变量捕获:重点重点重点
提问:上述代码能否将isQuit这个变量在main方法中定义(局部变量)。不行
在lambda匿名内部类当中有一条规定:lambda匿名内部类可以获取外面定义的成员变量(本质是将外面的成员变量作为参数传进来,这个被捕获的(变量)参数必须是final,或者“事实final”)
大前提这个变量定义在main函数内部
零:解释“事实final”
定义的成员变量虽然没有被final修饰,但是这个成员变量没有被修改过(有点那种“虽无夫妻之名,但有夫妻之实”的感觉)。
举例1:在mian函数内部定义 不加final修饰 的局部变量(可行)
举例2:在mian函数内部定义 不加final修饰 的局部变量 并且修改isQuit的值(不可行,lambda报错)
举例3:在mian函数内部定义 加final修饰 的局部变量 并且修改isQuit的值(不可行,final报错)
(2)lambda复制和传参理解
承接举例1:我们删除isQuit这一行代码
我们都知道,main函数执行完毕,那么main函数中定义的局部变量会进行回收,但是在上述两个线程中,main方法结束了,isQuit已经被回收了,这个Thread(t1)线程为什么还能运行(他线程用到了isQuit作为循环判断条件)。
总结:这里是因为,lambda获得的成员变量,是作为参数传进来的(可以理解为复制拷贝,是一个全新的个体),main方法执行完了,isQuit的栈帧就销毁了,但是Thread(t1)中拷贝过来的isQuit所创建的栈帧还在呀!!!
。
2:Thread内置变量isinterrupted
(1)isinterrupted本质
利用Thread类的实例内部成员变量,来取代我们在1中使用的,手动创建的静态成员变量isQuit
package thread;/*** Created with IntelliJ IDEA.* Description:* User: Hua YY* Date: 2024-09-20* Time: 10:23*/
public class ThreadDemon13 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{while(!Thread.currentThread().isInterrupted()){ //当t1.interrupt();生效,循环条件为false,被打断了try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("t1线程正在运行");}});t1.start();Thread.sleep(2001);t1.interrupt();//让t1被打断,相当于isQuit = trueSystem.out.println("终止t1线程");}
}
(2)sleep清空标志符
结果分析——
目的:给程序员创造更多的“可操作空间”
运行上述代码,报错,这是因为,我们的中断了t1线程的sleep(提前唤醒会做两件事:1:清空标志符,2:抛出InterruptedException异常),sleep把interrupted又设置回false,进而while循环判定条件变成true,最后被catch捕获中断异常。
(3)解决方式(catch中加代码)
①让线程立即结束:加上break;
②让线程不结束:不加break,让它报错
③让线程执行一些代码后再结束:写一些其它代码后,再break。
相关文章:

【JavaEE】线程创建和终止,Thread类方法,变量捕获(7000字长文)
阿华代码,不是逆风,就是我疯,你们的点赞收藏是我前进最大的动力!!7000字长文,希望本文内容能够帮助到你! 目录 一:创建线程五种方式 方式一:继承Thread类,…...

[Python数据可视化] Plotly:交互式数据可视化的强大工具
引言: 在数据分析和可视化的世界中,Plotly 是一颗耀眼的明星。它是一个开源的交互式图表库,支持多种编程语言,包括 Python、R 和 JavaScript。Plotly 的强大之处在于它能够创建出既美观又具有高度交互性的图表,使得数据…...

Excel--DATEDIF函数的用法及参数含义
DATEDIF函数的用法为: DATEDIF(start_date,end_date,unit),start_date表示的是起始时间,end_date表示的是结束时间。unit表示的是返回的时间代码,是天、月、年等。如下: Datedif函数的参数含义unit参数返回值的意义"y"两个时间段之间的整年数…...

执行网络攻击模拟的 7 个步骤
在进攻和防守策略方面,我们可以从足球队和美式足球队身上学到很多东西。球员们会分析对方球队的策略,找出弱点,相应地调整进攻策略,最重要的是,练习、练习、再练习。作为最低要求,网络安全部门也应该这样做…...

技术成神之路:设计模式(十四)享元模式
介绍 享元模式(Flyweight Pattern)是一种结构性设计模式,旨在通过共享对象来有效地支持大量细粒度的对象。 1.定义 享元模式通过将对象状态分为内部状态(可以共享)和外部状态(不可共享)…...
使用systemctl实现开机自启动jar包
目录 1. 创建服务文件2. 配置服务文件3. 重新加载 systemd 配置4. 启动服务5. 查看服务状态 1. 创建服务文件 创建服务文件: 在 /etc/systemd/system/ 目录下创建一个新的服务文件 myapp.service。 sudo vim /etc/systemd/system/myapp.service2. 配置服务文件 按i…...

2024.9.20营养小题【2】(动态分配二维数组)
这道题里边涉及到了动态分配二维数组的知识点,不刷这道题我也不知道这个知识点,算是一个比较进阶一点的知识点了。 参考:C语言程序设计_动态分配二维数组_哔哩哔哩_bilibili【C/C 数据结构 】二维数组结构解析 - 知乎 (zhihu.com)...

前端web端项目运行的时候没有ip访问地址
我们发现 没有netWork 的地址 导致 团队内其他同学无法打开我们的地址 进行访问 在page.json 中的运行 指令中 添加 --host 记得加上空格 这样我们就可以看到这个地址了 团队其他同学 就可以访问我们这个地址了...

微服务架构陷阱与挑战
微服务架构6大陷阱 现在微服务的基础设施还是越来越完善了,现在基础设施缺乏的问题逐渐被解决了。 拆分粒度太细,服务关系复杂 拆分降低了服务的内部复杂度,但是提升了系统的外部复杂度,服务越多,服务和服务之间的连接…...

react的事件绑定
文章目录 基本示例使用箭头函数事件对象阻止默认行为绑定事件处理函数的上下文 在 React 中,事件绑定主要通过 JSX 属性来实现。事件处理函数被传递给相应的事件属性,例如 onClick、onChange 等。这些属性会被绑定到 HTML 元素上,并在事件发生…...
ASP.NET Core 入门教学二十九 DDD设计
在软件开发中,领域驱动设计(Domain-Driven Design,简称DDD)是一种重要的软件设计方法论,它强调通过深入理解业务领域来构建高质量的软件系统。DDD的核心思想是将复杂的业务逻辑集中在领域模型中,并通过分层…...
Rocprofiler测试
Rocprofiler测试 一.参考链接二.测试过程1.登录服务器2.使用smi获取列表3.使用rocminfo获取Agent信息4.准备测试用例5.The hardware counters are called the basic counters6.The derived metrics are defined on top of the basic counters using mathematical expression7.P…...

基于python flask的高血压疾病预测分析与可视化系统的设计与实现,使用随机森林、决策树、逻辑回归、xgboost等机器学习库预测
研究背景 随着现代社会的快速发展,生活方式的改变和人口老龄化的加剧,心血管疾病,尤其是高血压,已成为全球范围内的重大公共健康问题。高血压是一种常见的慢性疾病,其主要特征是动脉血压持续升高。长期不控制的高血压…...
Lombok 与 EasyExcel 兼容性问题解析及建议
在 Java 开发中,Lombok 被广泛用于减少样板代码,如 Getter、Setter、构造函数等。然而,在与像 EasyExcel 这样依赖反射机制的库一起使用时,可能会遇到一些意想不到的问题。本文将深入探讨 Lombok 与 EasyExcel 之间的兼容性问题&a…...

Kubeadm快速安装 Kubernetes集群
1. Kubernetes简介 Kubernetes(k8s)是谷歌开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。它具有以下特点: 开源容器化自动部署扩展高可用 2. Kubernetes架构 Kubernetes遵循主从式架构设计,主要分…...
OpenJudge | 八皇后问题
总时间限制: 10000ms 内存限制: 65536kB 描述 在国际象棋棋盘上放置八个皇后,要求每两个皇后之间不能直接吃掉对方。 输入 无输入。 输出 按给定顺序和格式输出所有八皇后问题的解(见Sample Output)。 样例输入 (null)样例输出 No. 1 …...
C#往压缩包Zip文件的文件追加数据
C#往压缩包Zip文件的文件追加数据 往一个已经压缩好的压缩包里追加数据,一般就有两种方式,一种是前面已经学习过的,就是追加一个新的文件, 另外一种就是往已经存在的文件追加数据。 往已经存在的文件追加数据,需要先找到文件索引。 在压缩包里声明的名称,与外面的文件路…...
局域网共享文件夹:您没有权限访问,请与网络管理员联系
局域网共享文件夹:您没有权限访问,请与网络管理员联系 win10 1909 专业版背景 我有两个电脑,还有两块外挂硬盘,较大的一块放在老电脑上,为了方便用垃圾百度网盘在里边下载东西,又不污染新电脑的环境。 如…...

科技修复记忆:轻松几步,旧照变清晰
在时间的长河中,旧照片承载着无数珍贵的记忆与故事。然而,随着岁月的流逝,这些照片往往变得模糊不清,色彩黯淡,令人惋惜。 幸运的是,随着科技的发展,我们有了多种方法来修复这些旧照片的画质&a…...

java -versionbash:/usr/lib/jvm/jdk1.8.0_162/bin/java:无法执行二进制文件:可执行文件格式错误
实验环境:Apple M1在VMwareFusion使用Utubun Jdk文件错误  尝试: 1、重新在网盘下载java1.8 2、在终端通过命令下载 3、确保 JDK 正确安装在系统中,可以通过 echo $JAVA_HOME 检查 JAVA_HOME 环境变量是否设置正确。 ࿿…...
Python中openpyxl库的基础解析与代码实例
目录 1. 前言 2. 安装openpyxl 3. 创建一个新的工作簿 4. 打开一个已有的工作簿 5. 读取和写入单元格 6. 操作工作表 7. 样式设置 8. 插入图像 9. 插入图表 10. 数据验证 11. 条件格式 12. 工作簿保护 13. 保存和关闭工作簿 14. 总结 1. 前言 在数据分析和处理的…...

4.2.2 Spark SQL 默认数据源
在本实战概述中,我们探讨了如何在 Spark SQL 中使用 Parquet 格式作为默认数据源。首先,我们了解了 Parquet 文件的存储特性,包括其二进制存储方式和内嵌的 Schema 信息。接着,通过一系列命令,我们演示了如何在 HDFS 上…...
React与Vue的内置指令对比
React 与 Vue 在内置指令的设计理念和实现方式上有显著差异。Vue 提供了一套丰富的模板指令系统,而 React 更倾向于通过原生 JavaScript 语法和 JSX 实现类似功能。以下是两者的核心对比: 一、条件渲染 Vue 使用 “v-if”/ “v-else” 指令,…...

Unity基础学习(十二)Unity 物理系统之范围检测
目录 一、关于范围检测的主要API: 1. 盒状范围检测 Physics.OverlapBox 2. 球形范围检测 Physics.OverlapSphere 3. 胶囊范围检测 Physics.OverlapCapsule 4. 盒状检测 NonAlloc 版 5. 球形检测 NonAlloc 版 6. 胶囊检测 NonAlloc 版 二、关于API中的两个重…...

Linux 基础开发工具的使用
目录 前言 一:下载工具yum 二:文本编辑器vim 1. 命令模式 2. 插入模式 3. 底行模式 三:gcc和g 基本使用格式 常用选项及作用 编译过程示例 四、Linux 项目自动化构建工具 ——make/Makefile 1. make 与 Makefile 的关系 2. Make…...

Redis的大Key问题如何解决?
大家好,我是锋哥。今天分享关于【Redis的大Key问题如何解决?】面试题。希望对大家有帮助; Redis的大Key问题如何解决? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Redis中的“大Key”问题是指某个键的值占用了过多…...

NLua性能对比:C#注册函数 vs 纯Lua实现
引言 在NLua开发中,我们常面临一个重要选择:将C#函数注册到Lua环境调用,还是直接在Lua中实现逻辑? 直觉告诉我们,C#作为编译型语言性能更高,但跨语言调用的开销是否会影响整体性能?本文通过基准…...

vscode java debug terminal 中文乱码
现象 解决 快捷键 ctrl , 进入setting 配文件添加 "terminal.integrated.automationProfile.windows": {"path": "cmd","args": ["/k","chcp","65001"]}terminal 启动时,活动也改为 utf-…...

知行之桥如何将消息推送到钉钉群?
在钉钉平台中,机器人主要分为企业机器人和自定义机器人两类。本文将重点介绍如何通过自定义机器人,实现将知行之桥 EDI 系统的通知消息高效推送至钉钉群,帮助企业第一时间掌握业务动态。 一、在钉钉群中添加自定义机器人 在需要接收知行之桥…...
百度之星2024 初赛第一场 补给
百度之星2024 初赛第一场 补给 题干描述问题分析:C代码Java代码:Python代码补充说明: 题干描述 参考自马蹄集OJ,原文链接1 可怕的战争发生了,小度作为后勤保障工作人员,也要为了保卫国家而努力。 现在有 …...