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.hqwc.cn/news/543881.html

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

相关文章

[善用佳软]推荐掌握小工具:Json解析的命令行工具jq

前言&#xff1a; 我们在各种生产环境或者开发测试环境中&#xff0c;一定遇到有很多信息都是使用JSON串或者文本文件作为输入的。在没有JQ命令行工具之前&#xff0c;我们要从中获取真正的输入&#xff0c;大都把它复制到文本里头&#xff0c;然后使用文本编辑器进行加工整理…

TinyEMU之Linux Kernel编译

TinyEMU之Linux Kernel编译 1 准备工作2 安装RISC-V交叉编译器3 编译Linux Kernel4 镜像格式转换 本文属于《 TinyEMU模拟器基础系列教程》之一&#xff0c;欢迎查看其它文章。 1 准备工作 我们需要&#xff0c;下载以下内容。 编译好的RISC-V交叉编译器&#xff1a;riscv64-…

边缘计算网关的工作原理及其在工业领域的应用价值-天拓四方

随着物联网技术的快速发展&#xff0c;物联网时代已经悄然来临。在这个时代&#xff0c;数以亿计的设备相互连接&#xff0c;共享数据&#xff0c;共同构建智慧的世界。边缘计算网关通过将计算能力和数据存储推向网络的边缘&#xff0c;实现了对海量数据的实时处理&#xff0c;…

Vue命令式组件的编写与应用

目录 1.引言 2.传统的组件 3.命令式组件 4.命令式组件的应用场景 1.引言 大家好&#xff01;今天我们来聊聊Vue.js中的一个有趣话题——命令式组件。你有没有觉得&#xff0c;有时候我们在Vue模板里写组件&#xff0c;就像是在玩搭积木&#xff0c;每个积木都有固定的形状…

什么又是线程呢??

线程&#xff1a; 线程可以并发的执行&#xff0c;但是线程的地址是可以共享的 进程与线程的比较&#xff1a; 进程>线程 线程分三种&#xff1a; 用户线程 只有用户程序的库函数来 用户线程 因为操作系统感知不到 线程&#xff0c;如果有线程在运行&#xff0c;然后不交…

Doris基本SQL语句(官方使用指南轻量化)

Doris 采用 MySQL 协议进行通信&#xff0c;用户可通过 MySQL client 或者 MySQL JDBC连接到 Doris 集群。 选择 MySQL client 版本时建议采用5.1 之后的版本&#xff0c;因为 5.1 之前不能支持长度超过 16 个字符的用户名。 #修改root密码 mysql> SET PASSWORD FOR root P…

攻防世界-misc-Make-similar

题目链接&#xff1a;攻防世界 (xctf.org.cn) 下载得到ogg文件。Olympic CTF 2014原题有提示120 LPM&#xff0c;对应Radiofax。需要将ogg格式文件转换成wav格式音频后&#xff0c;用OS X下的软件Multimode转换成单色传真图像&#xff1a; 文字部分为&#xff1a; section 1 of…

【JVM】GCRoot

GC root原理 通过对枚举GCroot对象做引用可达性分析&#xff0c;即从GC root对象开始&#xff0c;向下搜索&#xff0c;形成的路径称之为 引用链。如果一个对象到GC roots对象没有任何引用&#xff0c;没有形成引用链&#xff0c;那么该对象等待GC回收。 可以作为GC Roots的对…

AcWing.505 火柴排队(离散化逆序对)

题目 涵涵有两盒火柴&#xff0c;每盒装有 n  根火柴&#xff0c;每根火柴都有一个高度。 现在将每盒中的火柴各自排成一列&#xff0c;同一列火柴的高度互不相同&#xff0c;两列火柴之间的距离定义为&#xff1a; ∑i1n(ai−bi)2 其中 ai表示第一列火柴中第 i个火柴的…

MQ 延迟队列

MQ 延迟队列 1. 前言 延迟队列是我们日常开发过程中&#xff0c;经常接触并需要使用到的一种技术方案。前些时间在开发业务需求时&#xff0c;我也遇到了一个需要使用到延迟消息队列的需求场景&#xff0c;因此我也在网上调研了一系列不同的延迟队列的实现方案&#xff0c;在…

在SwiftUI中使用Buider模式创建复杂组件

在SwiftUI中使用Buider模式创建复杂组件 我们在前面的博客闲聊SwiftUI中的自定义组件中聊到了如何在SwiftU中创建自定义组件。 在那里&#xff0c;我们创建了一个非常简单的组件RedBox&#xff0c;它将展示内容增加一个红色的边框。 RedBox非常简单&#xff0c;我们用普通的方…

Android Binder机制解析

一 binder介绍&#xff1a; binder机制是一种基于Client-Server架构的IPC&#xff08;Inter-Process Communication&#xff0c;进程间通信&#xff09;机制&#xff0c;它允许不同进程之间进行高效的通信和数据交换。Binder机制在Android系统中扮演着至关重要的角色&#xff…