javaEE——线程的等待和结束
文章目录
- Thread 类及常见方法
- 启动一个线程
- 中断一个线程
- 变量型中断
- 调用 interrupt() 方法来通知
- 观察标志位是否被清除
- 等待一个线程
- 获取当前线程引用
- 休眠当前线程
- 线程的状态
- 观察线程的所有状态
- 观察 1: 关注 NEW 、 RUNNABLE 、 TERMINATED 状态的切换
- 多线程带来的风险
- 为什么会这样?
Thread 类及常见方法
Thread类是我java给提供的一个线程类其内部包含了很多帮助我们的方法。除了上次讲述的初始化方法外还有哪些呢?请看下面的内容
启动一个线程
我们知道了通过复写run方法创建一个线程对象(在我的上一篇文章中说明过)但是线程被创建出来并不代表线程已经开始了运行。因此只有调用了start方法才是真正的创建了一个线程用法如下
public class Main {public static void main(String[] args) throws InterruptedException{Thread t=new Thread(()->{while(true){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("我是lambda表达式创建出的线程");}});t.start();while(true){Thread.sleep(1000);System.out.println("Hello world!");}}
}
中断一个线程
线程在执行的过程中不仅要知道如何进行启动,中断也同样重要因为线程的执行只有当线程这个任务彻底完成后才会中断但是这个机制是不好的因为这时候就会导致一些有问题的线程我们没法立刻进行中断,那么现在主要有哪些中断线程的方法呢?
变量型中断
使用一个自定义变量进行中断线程请看如下代码
class Mythread extends Thread{public void run(){while(Main.flag){try {sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("正常交易中");}System.out.println("有内鬼停止交易");}
}
public class Main {public static boolean flag=true;public static void main(String[] args) throws InterruptedException{Thread t=new Mythread();t.start();int cnt=0;while(true){Thread.sleep(1000);if(cnt<5)System.out.println("令线程正常交易");cnt++;if(cnt>=5){System.out.println("通知线程有内鬼");flag=false;break;}}}
}
请看上面这个代码这个代码很明显就是用了一个flag变量来控制这里面的while循环是否可以继续执行但是这个方法非常的不优雅,感觉有种很土的感觉,那难道我们的java就没有官方提出来一些方法来使用嘛?当然是有的。那就是下面三个方法
调用 interrupt() 方法来通知
| 方法 | 说明 |
|---|---|
| public void interrupt() | 中断对象关联的线程,如果线程正在阻塞,则以异常方式通知,否则设置标志位 |
| public static boolean interrupted() | 判断当前线程的中断标志位是否设置 调用后清除标志位 |
| public boolean isInterrupted() | 判断对象关联线程的标志位是否设置调用后不清除标志位 |
那么这些方法该怎么使用呢?请看如下代码
class Mythread extends Thread{public void run() {while(!Thread.interrupted()){try {sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("正常交易中");}System.out.println("有内鬼停止交易");}
}
public class Main {public static boolean flag=true;public static void main(String[] args) throws InterruptedException{Thread t=new Mythread();t.start();int cnt=0;while(true){Thread.sleep(1000);if(cnt<5)System.out.println("令线程正常交易");cnt++;if(cnt>=5){System.out.println("通知线程有内鬼");t.interrupt();break;}}}
}

在这个代码运行截图中我们可以看到即使抛出了异常但是这个代码仍然没有终止那么这是为什么呢?因为我们看一下上面的对这些的方法的介绍。interrupted()方法介绍说了调用本方法可以查看此时的标志位并且在查看过后就会清除标志位,那么按照这个代码的逻辑我们来看一下。首先我们调用了interrupt()方法设置了我们的标记位,并且检测到线程此时正在休眠因此我们以异常的形式进行了抛出,之后当调用interrupted()方法进行判断的时候此时标志位未被设置因此循环将会继续执行,那么难道就没有办法了吗?当然是有的,第一个办法就是将异常抛出(因为我们学过当我们将异常抛出的时候那么这个方法就不会继续往后执行了)(第二个办法就是在循环内部加一个break)
thread 收到通知的方式有两种:
- 如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通
知,清除中断标志
当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择
忽略这个异常, 也可以跳出循环结束线程. - 否则,只是内部的一个中断标志被设置,thread 可以通过
Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志
Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志
观察标志位是否被清除
观察标志位是否被清除,我们来讲述一下那两个方法
首先标志位大家可以理解是什么呢?其实就是我们第一种方法定义的一个boolean的变量一样那么清除标志位就相当于把这个标志位重新设置成了false那样子,设置标志位就相当于将其设置为true,那么带着这种理解我们来看一下这两种方法首先是public static boolean interrupted()这个方法就像是一个自动开关一样,当你检查的时候相当于把灯打开了,当你检查完毕后又把灯给重新随手关上了public boolean isInterrupted()而这个则是不会关闭。
等待一个线程
等待一个线程,什么是等待一个线程呢?其实就是等待一个线程结束,我们刚刚说过线程是并发执行的,但是有些时候我们希望这个线程是顺序的,有时又可以是并发那么该怎么做呢?那就需要线程的等待了。顾名思义线程的等待其实就是等待一个线程的任务进行完毕后再去执行接下来的代码,因此就实现了线程的顺序执行,那么该怎么完成呢?那就是用join方法请看下面的代码
class Mythread extends Thread{public void run() {int i=0;while(i<=6){try {sleep(1000);} catch (InterruptedException e) {e.printStackTrace();break;}i++;System.out.println("正常交易中");}//System.out.println("有内鬼停止交易");}
}
public class Main {public static boolean flag=true;public static void main(String[] args) throws InterruptedException{Thread t=new Mythread();t.start();t.join();System.out.println("线程已经结束");}
}
代码运行截图

这样我们就可以看的很清楚了线程是顺序执行的。
获取当前线程引用
这个方法就比较简单了可以了解一下代码如下
public class Main {public static boolean flag=true;public static void main(String[] args) throws InterruptedException{Thread t=Thread.currentThread();System.out.println(t);}
}
休眠当前线程
休眠当前线程其实就是我们用的sleep方法关于这个方法呢我们要知道实际的休眠时间是要大于你设置的休眠时间的。
线程的状态
线程的状态是一个枚举类型,Thread.State,那么都有哪些状态呢?我们来看一下以下代码
public class Main {public static boolean flag=true;public static void main(String[] args) throws InterruptedException{for(Thread.State state:Thread.State.values()){System.out.println(state);}}
}

那么我们接下来,来描述以下现成的 这些状态的含义
| 状态类型 | 状态的含义 |
|---|---|
| NEW | 线程被创建出来但并没有开始行动 |
| RUNNABLE | 可以工作的又分为正在工作或者即将开始工作 |
| BLOCKED | 排队等着其他的事情 |
| WAITING | 排队等着其他的事情 |
| TIMED_WAITING | 排队等待其他的事情 |
| TERMINATED | 工作已经完成 |
观察线程的所有状态
观察 1: 关注 NEW 、 RUNNABLE 、 TERMINATED 状态的切换
使用isAlive进行线程状态的观察
public class Main {public static boolean flag=true;public static void main(String[] args) throws InterruptedException{Thread t=new Thread(()->{for(int i=0;i<5;i++){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("正在运行中");}});t.start();while(true) {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(t.getState());}}
}

在运行截图中我们可以看到这里的线程状态有的时候是TIMED_WAITING有时候却又变成了RUNNABLE那么这是为什么会这样呢?真的是非常的奇怪其实原因很简单因为我们加的有sleep语句当我们调用state方法进行查看此时线程的状态的时候那么这个线程可能正在执行任务也有可能是正在sleep因此才会出现这种现象
多线程带来的风险
首先多线程会带来怎样的风险呢?我们来看一下下面的这个例子
class MyRun{public int run=0;public void Run(){run++;}
}
public class Main {public static boolean flag=true;public static void main(String[] args) throws InterruptedException{final MyRun m=new MyRun();Thread t1=new Thread(()->{for (int i=0;i<5000;i++)m.Run();System.out.println("t1执行完毕"+m.run);});Thread t2=new Thread(()->{for (int i=0;i<5000;i++)m.Run();System.out.println("t2执行完毕"+m.run);});t1.start();t2.start();t1.join();t2.join();System.out.println(m.run);}
}
他的运行结果是怎么样的呢看下图

另外我在写代码的时候有一个非常傻子的错误那就是把join没写上导致打印结果是0这里原因是因为忽略了线程的并发执行,有同样问题的同学可以共勉。
为什么会这样?
为什么会这样呢?按道理来说不应该是一万吗?为什么会发生这种情况呢?相信大部分同学都会冒出这样的疑问那么这是为什么呢?其实原因很简单因为对于++来说你看到的是一个操作其实他的底层是三个指令也就是三个操作正因为是三个操作这就导致了这个操作不是原子的,那么就可能出现我们数据库中类似于脏读的情况。那么剩下的内容我会在下一期文章中详细说明
希望往后的日子可以与所爱万般皆顺利。
相关文章:
javaEE——线程的等待和结束
文章目录 Thread 类及常见方法启动一个线程中断一个线程变量型中断调用 interrupt() 方法来通知观察标志位是否被清除 等待一个线程获取当前线程引用休眠当前线程 线程的状态观察线程的所有状态观察 1: 关注 NEW 、 RUNNABLE 、 TERMINATED 状态的切换 多线程带来的风险为什么会…...
sqlplus设置提示符
作为DBA,需要管理好多数据库,经常会有一台服务器安装多个oracle实例的情况,为避免误操作实例,我们需要在执行sqkplus前,先通过$ echo $ORACLE_SID或 SQL>select name from v$database查看当前实例,这样难…...
macbook删除软件只需几次点击即可彻底完成?macbook删除软件没有叉 苹果笔记本MacBook电脑怎么卸载软件? cleanmymac x怎么卸载
在MacBook的使用过程中,软件安装和卸载是我们经常需要进行的操作。然而,不少用户在尝试删除不再需要的软件时,常常发现这个过程既复杂又耗时。尽管MacOS提供了一些基本的macbook删除软件方法,但很多时候这些方法并不能彻底卸载软件…...
Unity WebGL ios 跳转URL
需求: WebGL跳转网址 现象: Application.OpenURL("https://www.baidu.com"); 这个函数在安卓上可以用,IOS 不管用 解决方案: 编写js插件,unity调用js函数,由js跳转网址 注意事项 : 插件后缀为.jsli…...
机器学习模型—XGBoost
机器学习模型—XGBoost XGBoost(Extreme Gradient Boosting)是由陈天奇等人于2014年提出的一个高效可扩展的梯度提升库。它在梯度提升框架的基础上进行了优化和改进,被广泛应用于机器学习竞赛和实际应用中 作为GBDT(Gradient Boosting Decision Tree)的扩展版本,XGBoost在算…...
在Swift中集成Socket.IO进行实时通信
在Swift中集成Socket.IO进行实时通信 实时通信是许多现代应用程序的重要组成部分,从聊天应用程序到协作平台。Socket.IO 是一个流行的库,用于在 Web 和移动应用程序中实现实时的双向通信。在本文中,我们将讨论如何使用 Socket.IO-Client-Swi…...
vue防止用户连续点击造成多次提交
中心思想:在第一次提交的结果返回前,将提交按钮禁用。 方法一:给提交按钮加上disabled属性,在请求时先把disabled属性改成true,在结果返回时改成false 方法二:添加loading遮罩层,可以直接使用e…...
upload-labs通关方式
pass-1 通过弹窗可推断此关卡的语言大概率为js,因此得出两种解决办法 方法一 浏览器禁用js 关闭后就逃出了js的验证就可以正常php文件 上传成功后打开图片链接根据你写的一句话木马执行它,我这里采用phpinfo() 方法二 在控制台…...
本地用AIGC生成图像与视频
最近AI界最火的话题,当属Sora了。遗憾的是,Sora目前还没开源或提供模型下载,所以没法在本地跑起来。但是,业界有一些开源的图像与视频生成模型。虽然效果上还没那么惊艳,但还是值得我们体验与学习下的。 Stable Diffu…...
java 如何使用Lambda表达式实现递归和循环的替代品
java 如何使用Lambda表达式实现递归和循环的替代品 在Java中,Lambda表达式通常用于实现函数式接口,即只有一个抽象方法的接口。然而,Lambda表达式本身并不直接支持递归或循环。递归和循环是编程中的基本控制结构,通常通过方法调用…...
由浅到深认识C语言(12):位段/位域
该文章Github地址:https://github.com/AntonyCheng/c-notes 在此介绍一下作者开源的SpringBoot项目初始化模板(Github仓库地址:https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址:https://blog.csdn…...
antd5 虚拟列表原理(rc-virtual-list)
github:https://github.com/react-component/virtual-list rc-virtual-list 版本 3.11.4(2024-02-01) 版本:virtual-list-3.11.4 Development npm install npm start open http://localhost:8000/List 组件接收 Props PropDescriptionTypeDefaultchildrenRender …...
机器学习-04-分类算法-03KNN算法
总结 本系列是机器学习课程的系列课程,主要介绍机器学习中分类算法,本篇为分类算法与knn算法部分。 本门课程的目标 完成一个特定行业的算法应用全过程: 懂业务会选择合适的算法数据处理算法训练算法调优算法融合 算法评估持续调优工程化…...
Learn OpenGL 08 颜色+基础光照+材质+光照贴图
我们在现实生活中看到某一物体的颜色并不是这个物体真正拥有的颜色,而是它所反射的(Reflected)颜色。物体的颜色为物体从一个光源反射各个颜色分量的大小。 创建光照场景 首先需要创建一个光源,因为我们以及有一个立方体数据,我们只需要进行…...
springboot多模块下swaggar界面出现异常(Knife4j文档请求异常)或者界面不报错但是没有显示任何信息
继上一篇博文,我们解决了多模块下扫描不到子模块的原因,建议先看上一个博客了解项目结构: springboot 多模块启动报错Field XXX required a bean of type XXX that could not be found. 接下来我们来解决swaggar异常的原因,我们成功启动项目…...
【系统架构设计师】系统工程与信息系统基础 01
系统架构设计师 - 系列文章目录 01 系统工程与信息系统基础 文章目录 系列文章目录 前言 一、系统工程 ★ 二、信息系统生命周期 ★ 信息系统建设原则 三、信息系统开发方法 ★★ 四、信息系统的分类 ★★★ 1.业务处理系统【TPS】 2.管理信息系统【MIS】 3.决策支持系统…...
python自动化之(django)(2)
1、创建应用 python manage.py startapp apitest 这里还是从上节开始也就是命令行在所谓的autotest目录下来输入 然后可以清楚的看到 多了一个文件夹 2、创建视图 在views中加入test函数(所建应用下) from django.http import HttpResponse def tes…...
C语言 内存函数
目录 前言 一、memcpy()函数 二、memmove()函数 三、memset函数 四、memcmp()函数 总结 前言 在C语言中内存是我们用来存储数据的地址,今天我们来讲一下C语言中常用的内存函数。 一、memcpy()函数 memcpy()函数与我们之前讲的strcpy()函数类似,只…...
145 Linux 网络编程1 ,协议,C/S B/S ,OSI 7层模型,TCP/IP 4层模型,
一 协议的概念 从应用的角度出发,协议可理解为“规则”,是数据传输和数据的解释的规则。 典型协议 传输层 常见协议有TCP/UDP协议。 应用层 常见的协议有HTTP协议,FTP协议。 网络层 常见协议有IP协议、ICMP协议、IGMP协议。 网络接口层 常…...
【Java】List, Set, Queue, Map 区别?
目录 List, Set, Queue, Map 区别? Collection和Collections List ArrayList 和 Array区别? ArrayList与LinkedList区别? ArrayList 能添加null吗? ArrayList 插入和删除时间复杂度? LinkedList 插入和删除时间复杂度&…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
