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这里原因是因为忽略了线程的并发执行,有同样问题的同学可以共勉。

为什么会这样?

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java serlvet 高校学生画像平台系统Myeclipse开发mysql数据库web结构java编程计算机网页项目echarts图形展现

一、源码特点 java serlvet 高校学生画像平台系统是一套完善的java web信息管理系统 系统采用serlvetdaobean 模式开发本系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCA…

Selenium基础

1. selenium简介 用于实现自动化测试的 python 包&#xff0c;使用前需要安装对应浏览器驱动 from time import sleep from selenium import webdriver option webdriver.ChromeOptions() # 指定chrome存储路径的二进制形式 option.binary_locationD:\Chrome\Google\Chrome\Ap…

Android VINF

周末搞这玩意欲仙欲死&#xff0c;没办法只有看看。VINTF是供应商接口对象&#xff08;VINTF 对象&#xff09;&#xff0c;准确的说&#xff0c;这个是属于兼容性矩阵概念。。。有点想起了以前看过的一个电影&#xff0c;异次元杀阵。。。下面是谷歌官方的图。 本质上其实就是…

【分布式websocket 】前端vuex管理客户端消息crud!使用localStorage来存储【第19期】

前言 聊天系统客户端是要存储消息的&#xff0c;因为所有所有的历史消息都从服务器拉的话一方面服务器压力大&#xff0c;另一方面也耗费用户流量。所以客户端存储消息是势在必行的。如何存储呢上一篇文章也写了&#xff0c;大概就是浏览器的话是localStorage或者IndexedDB。然…

解决Linux中Eclipse启动时找不到Java环境的问题

按照报错的意思是没有在/usr/local/eclipse/jre/bin/java下找到java环境&#xff0c;我检查了一下eclipse的目录结构发现在/usr/local/eclipse没有jre/bin/java&#xff0c;我的想法是自己建对应文件夹然后软连接到我的java环境 cd /usr/local/eclipse sudo mkdir jre cd jre s…

基于Struts开发物流配送(快递)管理系统

开发工具&#xff1a;EclipseJdkTomcatMySQL数据库

使用Lua编写Wireshark解析ProtoBuf插件

文章目录 Wireshark Protobuf Lua-dissectorStep 1: 获取 WiresharkStep 2: 配置ProtoBuf相关设置添加ProtoBuf查找路径 Step 3 运行和调试Lua代码1. 添加Lua脚本2. 运行和调试 Step 4: 写Lua Dissector代码 :)Step 5(Optional): Decode AsGithub工程地址 Wireshark Protobuf L…

最新潮乎盲盒系统源码,附搭建教程

搭建方法 宝塔创建网站&#xff0c;上传后端程序到根目录&#xff0c;在.env修改数据库账号密码 上传数据库&#xff0c;伪静态thinkphp 运行目录public PHP扩展安装下面的 禁用函数先禁用下面那个&#xff0c;就可以了 前端是uniapp 后台admin 禁用函数putenv、 扩展fileinfo…

python问题:vscode切换环境,pip安装库网络错误

python问题&#xff1a;vscode切换环境&#xff0c;pip安装库网络错误 vscode切换环境pip安装库网络错误 记录一下遇见的python问题。 vscode切换环境 在vscode上面的搜索框输入 > select interpreter然后选择需要的环境。 pip安装库网络错误 用requirements.txt来安装…

【目标检测】原始的 YOLOv1 网络结构(GoogLeNet 作为 backbone 的实现)

现在看网上的很多 YOLOv1 的代码实现&#xff0c;基本都是使用新的 backbone&#xff0c;例如 ResNet 或者 VGG 来实现的&#xff0c;因为这些后面的通用的 backbone 可能比较方便的获得预训练模型&#xff0c;不需要从头开始训练。 但是我就是想看一下&#xff0c;一开始 YOL…

IntelliJ IDEA 面试题及答案整理,最新面试题

IntelliJ IDEA中的插件系统如何工作&#xff1f; IntelliJ IDEA的插件系统工作原理如下&#xff1a; 1、插件架构&#xff1a; IntelliJ IDEA通过插件架构扩展其功能&#xff0c;插件可以添加新的功能或修改现有功能。 2、安装和管理&#xff1a; 通过IDEA内置的插件市场下载…

游戏服务端配置“热更”及“秒启动”终极方案(golang/ygluu/卢益贵)

游戏服务端配置“热更”及“秒启动”终极方案&#xff08;golang/ygluu/卢益贵&#xff09; 关键词&#xff1a;游戏微服务架构、游戏服务端热更、模块化解耦、golang 一、前言 众所周知&#xff0c;游戏服务端配置信息热更有几大问题&#xff08;非lua架构&#xff09;&…

电脑那个部件坏了或者是哪个软件需要修复来看价钱

电脑维修价格表是多少&#xff1f; 价格取决于计算机的哪个部分损坏或哪个软件需要修复。 由于电脑中的部件非常多&#xff0c;而且会以各种奇怪的方式出现问题&#xff0c;下面我们就来看看具体的充电方法。 电脑维修价格表&#xff1a; 1. 重新安装系统。 安装XP系统通常需…

Tomcat Session 集群 ---------会话保持

一、 负载均衡、反向代理 环境搭建&#xff1a; nginx服务器192.168.246.7 tomcat 1服务器192.168.246.8 tomcat 2服务器192.168.246.9 7-1 nginx服务器搭建 [rootzzcentos1 ~]#systemctl stop firewalld [rootzzcentos1 ~]#setenforce 0 [rootzzcentos1 ~]#yum install …

Visual Studio配置libtorch(cuda安装一步到位)

Visual Studio配置libtorch visual Studio安装cuDNN安装CUDAToolkit安装libtorch下载Visual Studio配置libtorch(cuda版本配置) visual Studio安装 visual Studio点击安装 具体的安装和配置过程这里就不进行细讲了&#xff0c;可以参考我这篇博客Visual Studio配置OpenCV(保姆…

openCV实现拖拽虚拟方块

一、项目效果&#xff1a; 二、核心流程&#xff1a; openCV读取视频流、在每一帧图片上画一个矩形。使用mediapipe获取手指关键点坐标。根据手指坐标位置和矩形的坐标位置&#xff0c;判断手指点是否在矩形上&#xff0c;如果在则矩形跟随手指移动。 三、代码流程&#xff1…

基于SpringBoot框架实现的B2B平台的医疗病历交互系统

采用技术 基于SpringBoot框架实现的B2B平台的医疗病历交互系统的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBootMyBatis 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 页面展示效果 管理员角色 医院管理 医院注册 医院文…

C语言—打印如图矩阵

输出矩阵 在一个二维数组中形成并输出如下矩阵: #include <stdio.h> main() { int i,j,a[5][5];for(i0;i<4;i)for(j0;j<4;j)if(i<j) a[i][j]1;else a[i][j]i-j1;for(i0;i<4;i){ for(j0;j<4;j)printf("%d ",a[i][j]);printf("…

介绍一款鼠标无边界软件

"Mouse without Borders" 是一款由微软开发的免费工具&#xff0c;旨在帮助用户在多台计算机之间实现无缝的鼠标和键盘共享。通过 Mouse without Borders&#xff0c;用户可以在一个主控制台上控制多台计算机&#xff0c;就像操作一个大型虚拟桌面一样。 这个工具可…

zookeeper基础学习之六: zookeeper java客户端curator

简介 Curator是Netflix公司开源的一套zookeeper客户端框架&#xff0c;解决了很多Zookeeper客户端非常底层的细节开发工作&#xff0c;包括连接重连、反复注册Watcher和NodeExistsException异常等等。Patrixck Hunt&#xff08;Zookeeper&#xff09;以一句“Guava is to Java…