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 收到通知的方式有两种:

  1. 如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通
    知,清除中断标志
    当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择
    忽略这个异常, 也可以跳出循环结束线程.
  2. 否则,只是内部的一个中断标志被设置,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这里原因是因为忽略了线程的并发执行,有同样问题的同学可以共勉。

为什么会这样?

为什么会这样呢?按道理来说不应该是一万吗?为什么会发生这种情况呢?相信大部分同学都会冒出这样的疑问那么这是为什么呢?其实原因很简单因为对于++来说你看到的是一个操作其实他的底层是三个指令也就是三个操作正因为是三个操作这就导致了这个操作不是原子的,那么就可能出现我们数据库中类似于脏读的情况。那么剩下的内容我会在下一期文章中详细说明

	希望往后的日子可以与所爱万般皆顺利。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/749919.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【大模型系列】问答理解定位(Qwen-VL/Llama2/GPT)

文章目录 1 Qwen-VL(2023, Alibaba)1.1 网络结构1.2 模型训练 2 Llama2(2023, Meta)2.1 网络结构2.1.1 MHA/GQA/MQA2.1.2 RoPE(Rotary Position Embedding, 旋转式位置编码)2.1.3 RMSNorm 2.2 推理2.2.1 集束搜索(beam search)2.2.2 RoPE外推 3 GPT系列(OpenAI) 1 Qwen-VL(2023…

android中单例模式为什么会引起内存泄漏?

单例模式使用不恰当会造成内存泄漏。因为单例的静态特性使得单例的生命周期和应用的生命周期一样长&#xff0c; 如果一个对象已经不需要使用了&#xff0c;但是单例对象还持有该对象的引用&#xff0c;那么这个对象就不能被正常回收&#xff0c;因此会导致内存泄漏。 举个例子…

【数据可视化】使用Python + Gephi,构建中医方剂关系网络图!

代码和示例数据下载 前言 在这篇文章中&#xff0c;我们将会可视化 《七版方剂学》 的药材的关系&#xff0c;我们将使用Python制作节点和边的数据&#xff0c;然后在Gephi中绘制出方剂的网络图。 Gephi是一个专门用于构建网络图的工具&#xff0c;只要你能提供节点和边的数…

机器学习算法在数据挖掘中的应用

在数据挖掘的实践中&#xff0c;各种机器学习算法都扮演着重要的角色&#xff0c;它们能够从数据中学习规律和模式&#xff0c;并用于预测、分类、聚类等任务。以下是几种常见的机器学习算法以及它们在数据挖掘任务中的应用场景和优缺点。 1. 决策树&#xff08;Decision Tree…

Golang的CSP模型讲解

一.CSP是什么 CSP 是 Communicating Sequential Process 的简称&#xff0c;中文可以叫做通信顺序进程&#xff0c;是一种并发编程模型&#xff0c;是一个很强大的并发数据模型&#xff0c;是上个世纪七十年代提出的&#xff0c;用于描述两个独立的并发实体通过共享的通讯chann…

Stable Diffusion科普文章【附升级gpt4.0秘笈】

随着人工智能技术的飞速发展&#xff0c;我们越来越多地看到计算机生成的艺术作品出现在我们的生活中。其中&#xff0c;Stable Diffusion作为一种创新的图像生成技术&#xff0c;正在引领一场艺术创作的革命。本文将为您科普Stable Diffusion的相关知识&#xff0c;带您走进这…

微信小程序睡眠X秒【while循环模式】

// 微信小程序睡眠X秒sleep(numberMillis) { var now new Date(); var exitTime now.getTime() numberMillis; while (true) { now new Date(); if (now.getTime() > exitTime) {return;}} }, // 微信小程序睡眠X秒 this.sleep(2000); 参考&#xff1a;微信小程序睡眠…

Linux/Ubuntu/Debian控制台启动的程序和terminal分离的方法-正在运行怎么关闭窗口

disown 是一个 shell 内置函数&#xff0c;它从 shell 的作业表中删除指定的作业&#xff0c;使它们免受挂起的影响。 使用方法如下&#xff1a; 首先&#xff0c;正常运行命令&#xff1a; 你的命令然后&#xff0c;按 Ctrl Z 暂停命令。 现在&#xff0c;运行&#xff…

MT1069 圆切平面

n个圆最多把平面分成几部分&#xff1f;输入圆的数量N&#xff0c;问最多把平面分成几块。比如一个圆以把一个平面切割成2块。 不考虑负数&#xff0c;0或者其他特殊情况。 格式 输入格式&#xff1a;输入为整型 输出格式&#xff1a;输出为整型 样例 1 输入&#xff1a; …

全量知识系统“全基因序列” 的百度AI答问 之1

在您所描述的框架下&#xff0c;我们可以开始探索“知识”实体的起点以及如何认识它。首先&#xff0c;让我们明确一下“实体”的定义和性质。实体&#xff0c;在哲学和许多其他学科中&#xff0c;通常被理解为存在于我们世界中的具体事物或抽象概念。它们可以是物理的&#xf…

Flink 集群部署模式

文章目录 前言一、会话模式&#xff08;Session Mode&#xff09;二、单作业模式&#xff08;Per-Job Mode&#xff09;三、应用模式&#xff08;Application Mode&#xff09; 前言 Flink支持多种集群部署模式&#xff0c;以满足不同场景和需求。以下是Flink的主要集群部署模…

springboot多模块下swaggar界面出现异常(Knife4j文档请求异常)或者界面不报错但是没有显示任何信息

继上一篇博文&#xff0c;我们解决了多模块下扫描不到子模块的原因,建议先看上一个博客了解项目结构&#xff1a; springboot 多模块启动报错Field XXX required a bean of type XXX that could not be found. 接下来我们来解决swaggar异常的原因&#xff0c;我们成功启动项目…

QML 布局管理器之ColumnLayout

一.ColumnLayout讲解 QML中的ColumnLayout是一种布局元素&#xff0c;用于在垂直列中排列其子元素。它的主要使用下列附加属性: Layout.minimumWidth Layout.minimumHeight Layout.preferredWidth Layout.preferredHeight Layout.maximumWidth Layout.maximumHeight Layout.fil…

代码随想录算法训练营 Day25|回溯算法2

216.组合总和III 思路 按照回溯算法的逻辑&#xff0c;写代码。 递归函数的参数&#xff0c;除了原有的n, k, startIndex&#xff0c;增加一个sum&#xff0c;计算当前path中的和。 终止条件用sum与n比较&#xff0c;如果满足相等&#xff0c;且满足长度为k&#xff0c;则加入…

WPF-后台设置控件Background

有时候需要在后台设置控件的背景 方法1&#xff1a; Btn_SendNeedle_Admin.Content "送针结束"; Btn_SendNeedle_Admin.Background new SolidColorBrush((Media.Color)Media.ColorConverter.ConvertFromString("#AAFFFFFF")); 方法2&#xff1a; Btn…

SqlServer2008(R2)(一)SqlServer2008(R2)经典宝藏操作收集整理

一、常见操作 1、TRUNCATE TABLE 语句 删除表数据 TRUNCATE TABLE语句比DELET删除表中的所有行更快。从逻辑上讲&#xff0c;TRUNCATE TABLE它类似于DELETE没有WHERE子句的语句。 TRUNCATE TABLE语句从表中删除所有行&#xff0c;但表结构及其列&#xff0c;约束&#xff0c;…

JSON 配置文件

JSON 配置文件的作用 JSON 是一种数据格式&#xff0c;在实际开发中&#xff0c; JSON 总是以配置文件的形式出现。小程序项目中也不例外&#xff1a;通过不同的 .json 配置文件&#xff0c;可以对小程序项目进行不同级别的配置。 小程序项目中有 4 种 json 配置文件&#xff0…

1.AD域控如何强制删除不可以用域控服务器

(1)原因需求 (2)不可用的域控不删掉造成的问题 (3)实战配置步骤 第一步:连接登录到特定服务器 第二步:选择要删除域控所在的站点和名称 第三步:执行删除命令

Ubuntu 20.04 系统如何优雅地安装NCL?

一、什么是NCL&#xff1f; NCAR Command Language&#xff08;NCL&#xff09;是由美国大气研究中心&#xff08;NCAR&#xff09;推出的一款用于科学数据计算和可视化的免费软件。 它有着非常强大的文件输入和输出功能&#xff0c;可读写netCDF-3、netCDF-4 classic、HDF4、b…

Elasticsearch快速检索的法宝: 倒排索引

倒排索引&#xff08;Inverted Index&#xff09;是搜索引擎和信息检索系统中的一个关键数据结构&#xff0c;它允许快速进行全文搜索。在倒排索引中&#xff0c;文档的内容被分析并分割成一系列的词条&#xff08;tokens&#xff09;&#xff0c;然后每个词条被映射到包含它的…