JavaEE(系列3) -- 多线程(线程的中断与线程等待)

news2024/11/27 13:35:52

 新内容开始之前,我们总结一个知识点.

Thread类中的start方法和run方法的区别?

start():

        用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行, 然后通过此Thread类调用方法run()来完成其运行操作的, 这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程。 一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止.

run():

        run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。

总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run()方法,这是由jvm的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void。

目录

1. Thread 类的方法 

2. 线程中断

2.1 设置一个结束标志位

2.2 interrupt() (Thread 中自带的标志位) 

2.3 为什sleep要清空标志位?

3.线程等待


1. Thread 类的方法 

2. 线程中断

线程的中断,就是将一个线程停下来.

线程的终止:本质上只有一种,就是让该线程的入口方法执行完毕.

2.1 设置一个结束标志位

/**
 * Created with IntelliJ IDEA.
 * Description:线程中断1-自己创建变量,控制循环
 * User: YAO
 * Date: 2023-05-08
 * Time: 16:28
 */
public class ThreadDemo7 {

    public static boolean isQuit = false;
    public static void main(String[] args) {

        Thread thread = new Thread(()->{
            while (!isQuit){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("新线程结束");
        });

        thread.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        isQuit = true;
    }
}

通过主线程进行修改标志位,使得新线程中的代码停止了循环.

思考:此时如果将isQuit,不写成成员变量,写在Main方法之中,还会达到想要的效果吗? 

答案:不可以 虽然lambda表达式能够访问局部变量,但是lambda表达式有变量捕获,捕获的变量必须是没有进行修改过的变量,main方法中后面对isQuit做了修改,所以不可以写在main方法中.这个变量要么是被final修饰,如果不是被final修饰的 你要保证在使用之前,没有修改. 

2.2 interrupt() (Thread 中自带的标志位) 

public class ThreadDemo8 {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            //currentThread()  获取当前线程的实例
            while (!Thread.currentThread().isInterrupted()){
                System.out.println("hello t");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();//打印当前异常位置的调用栈
  
                }
            }
        });

        thread.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //将标志位设置为true
        thread.interrupt();
    }
}

简要介绍一下上面的代码.

首先认识两个方法:

1. Thread.currrentThread() :是获取当前线程的实例.

2. Thread.currrentThread() .isInterrupted()  --->默认是false

 

interrupt方法的作用:
* 1.设置标志位为true
* 2.如果该线程处于堵塞的状态(比如正在执行sleep),此时就会把阻塞状态给唤醒.通过抛出异常的方式让sleep立即结束.

当我们运行上述代码的时候,我们会看见下述问题  

 

出现的原因:

主线程中通过设置标志位interrupt,就立刻将sleep给唤醒,当sleep被唤醒的时候,sleep会自动的把isInterrupted标志位给清空  (true->flase),这样新线程的循环就被打通了,继续的执行sleep下一次遇见的时候就标志位就是false,那么就执行sleep本身,也不抛出异常,循环一直走下去,如果想要该循环截止,就在抛出异常的时候加一个break.

多线程的代码执行顺序不是“从上到下”,而是每个线程独立执行的。

2.3 为什sleep要清空标志位?

 清空标志位的目的是让线程本身能够对于线程何时结束,有一个更明确的控制.

当前的interrupt方法效果,并不是让线程立即结束,而是告诉他,你该结束了,至于他是否真的要结束,立即结束还是等会结束,都是代码来灵活控制的。

interrupt只是建议,不是命令。为什么java不强制设定为“命令结束”的操作呢?只要调用interru就立即结束呢?因为设定成这种,非常不友好。线程t何时结束,一定是t自己最清楚。交给t自己决定,就比较好。

3.线程等待

使用thread.join()

线程之间是并发执行的。操作系统对于线程的调度是无序的,无法判定两个线程谁先执行结束,谁后执行结束。

package threading;
 
public class ThreadDemo8 {
    public static void main(String[] args) {
        Thread t=new Thread(()->{
            System.out.println("hello t");
        });
        t.start();
        System.out.println("hello main");
    }
}

 上述代码中,先输出hello main 还是先输出hello t是无法确定的,大概率先输出hello main.必定创建线程是需要开销的,但是我们海狮无法确定优先顺序,这就引出一个问题,当我们需要一个场景就是,必须等待线程2执行完了再执行线程1,这时候我们怎么办呢?

使用关键字 join

public class ThreadDemo9 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            System.out.println("hello t");
        });

        thread.start();

        System.out.println("hello main1");
        //在main线程中调用thread.join,main线程等待Thread线程,等Thread执行完了之后,在继续执行main线程.
        thread.join();
        System.out.println("hello main2");
    }
}

上述代码的意思是,在主线程中加了一个t.join,就是等待t线程结束之后,再执行t.join之后的main线程中的代码.也就是在t线程结束之前,main线程处于一个堵塞的状态,暂时不参加CPU的调度执行.运行结果如下:

补充: 

join还有一个版本,可以填写一个参数,作为“超时时间”,等待的最大时间。

join的无参数版本,效果就是“死等”,不见不散。

join的有参数版本,则是执行最大超时时间,如果等待的时间到了上限,还没有等到,咱就不等了。

 那么此时就会联想到一个问题,会不会出现两个线程互相等待的情况.

 

答案:是会出现的 那种情况称为死锁,是bug。  比如家钥匙锁车里了,车钥匙锁家里了。

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

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

相关文章

Nexus下载与安装详解

目前 Nexus 分为 Nexus 2.x 和 Nexus 3.x 两个大版本,在以往框架,有了3.x一定不会再更新2.x了,会继续向上延续3.x,4.x,而Nexus对于这两个版本是并行的关系。也就是2.x在更新迭代,3.x也在更新迭代&#xff0…

K8s基础12——etcd数据备份与恢复、集群版本升级、网络策略

文章目录 一、etcd备份与恢复1.1 kubeadm部署方式1.1.1 备份1.1.2 恢复 1.2 单etcd二进制部署方式1.2.1 部署1.2.2 备份1.2.3 恢复1.2.4 K8s集群二进制部署方式恢复 二、集群版本升级2.1 升级master节点2.2 升级node节点 三、网络策略3.1 核心字段释义3.2 测试案例案例1&#x…

20 KVM管理虚拟机-虚拟机生命周期示例

文章目录 20 KVM管理虚拟机-虚拟机生命周期示例20.1 创建虚拟机20.2 启动虚拟机20.3 重启虚拟机20.4 关闭虚拟机20.5 销毁虚拟机 20 KVM管理虚拟机-虚拟机生命周期示例 本节给出虚拟机生命周期管理相关命令的示例。 20.1 创建虚拟机 虚拟机XML配置文件为openEulerVM.xml # …

shellCode免杀技巧

目录 一、免杀简述 二、免杀方法 1.shellcode反转bypass 2.shellcode异或bypass 3.远程加载shellcode bypass 4.进程注入 5.未导出api bypass 6.掩日(进程注入工具) 其他的 本文章仅提供学习,切勿将其用于不法手段! 一、…

《编码——隐匿在计算机软硬件背后的语言》精炼——第17章(自动操作)

夫道成于学而藏于书,学进于振而废于穷。 文章目录 完善加法器加入代码的加法器扩大加数范围自由调用地址的加法器合并代码RAM和数据RAMJump指令硬件实现条件Jump指令零转移的硬件实现条件Jump指令的例子 总结 完善加法器 我们在第14章介绍了一个可以进行连加的加法…

在线域名批量查询工具-未注册域名批量查询软件

在线域名批量查询工具 在线域名批量查询工具是一种通过互联网进行批量查询域名相关信息和指标的工具。以下是其主要特点: 在线查询:在线域名批量查询工具可以直接在浏览器中进行查询,无需下载和安装任何软件。 批量查询:该工具…

内网渗透--frp代理设置与proxychains代理设置

标题内网渗透–frp代理设置与proxychains代理设置 内网服务器内网IP地址外网IP地址内网web服务器(windows 7)192.168.52.143192.168.213.138内网域控服务器192.168.52.138 外网服务器外网IP地址外网V8网卡外网kali192.168.213.132外网windows攻击机192…

开通小程序账号

文章目录 一、开通小程序账号1.1 登录微信公众平台注册小程序管理员账号1.2 激活邮箱1.3 信息登记 二、获取开发设置2.1 获取APP ID2.2 获取AppSecret 一、开通小程序账号 微信小程序已经成为移动应用开发的热门平台之一,许多开发者都想要开发自己的小程序。但是首先我们需要注…

Pandas中的逻辑运算符(与或非)及Python代码示例

Pandas是Python中一个非常流行的用于数据处理和分析的库,它提供了大量的函数和操作符,以便用户可以方便地对数据进行操纵。其中逻辑运算符是在Pandas中经常使用的一些操作符之一,因为它们使我们可以对数据进行逻辑上的比较和筛选。本篇博客将…

【Dart】=> [01] Dart基础-下载安装环境配置

目录 windows下载安装地址1. 下载dart-sdk并且解压到某盘符目录下2. 找到bin目录,复制bin目录完整路径3. 打开我的电脑,右键菜单,点击属性4. 找到高级系统设置,点击5. 点击环境变量![在这里插入图片描述](https://img-blog.csdnim…

串口监控的几种方式

目录 方法1. 使用usb转TTL模块硬件监控; 方法2. 使用JLINK的SWD接口的串口收发脚进行硬件监控; 方法3. 使用虚拟串口进行软件监控; 方法1. 使用usb转TTL模块硬件监控; 方法2. 使用JLINK的SWD接口的串口收发脚进行硬件监控&…

第十二章 使用Bind提供域名解析服务

文章目录 第十二章 使用Bind提供域名解析服务一、DNS域名解析服务1、DNS简介2、服务器类型3、13台根DNS服务器的具体信息 二、安装Bind服务程序1、Bind简介2、Bind安装3、关键配置文件4、修改主配置文件5、正向解析实验(1)、编辑区域配置文件&#xff08…

必须了解的内存屏障

目录 一,内存屏障1,概念2,内存屏障的效果3,cpu中的内存屏障 二,JVM中提供的四类内存屏障指令三,volatile 特性1,保证内存可见性定义2,禁止指令重排序3,不保证原子性 一&a…

Http与Https 比较

目录 1、HTTP(HyperText Transfer Protocol:超文本传输协议) 2、HTTPS(Hypertext Transfer Protocol Secure:超文本传输安全协议) 3、HTTP 与 HTTPS 区别 4、HTTPS 的工作原理 1、HTTP(HyperTex…

MySQL---存储过程(局部变量、用户变量、系统变量(全局变量、会话变量)、传参(in、out、inout))

1. 存储过程特性 存储过程就是数据库 SQL 语言层面的代码封装与重用。 有输入输出参数,可以声明变量,有if/else, case,while等控制语句,通过编写存储过程,可以实现 复杂的逻辑功能; 函数的普遍特性:模块…

全球特种无人机市场规模逐渐扩大,预计今年将突破120亿美元

翱翔于空中是人们长久的追求,1903年,莱特兄弟发明了第一家螺旋桨飞机,这次飞行标志着飞机时代的开始。科技发展到今天,无人机(英文简称为“UAV”)作为一种高科技产品已经逐渐走进人们的生活中。 无人机技术…

如何解决浏览器跨域问题?

浏览器判断是跨域请求会在请求头上添加origin,表示这个请求来源哪里。比如: Plaintext GET / HTTP/1.1 Origin: http://localhost:8601服务器收到请求判断这个Origin是否允许跨域,如果允许则在响应头中说明允许该来源的跨域请求,…

【windows批处理batch】批处理batch字符串处理相关操作(字符串定义、分割、拼接、替换、切片、查找)

👉博__主👈:米码收割机 👉技__能👈:C/Python语言 👉公众号👈:测试开发自动化 👉专__注👈:专注主流机器人、人工智能等相关领域的开发、…

从万和电气看民企如何获得“新增长”

改革开放至今,中国民营企业在经过蒙眼狂奔的三十多年后,普遍迎来发展难题: 1,第一代创业者普遍进入退休年龄,面临代际传承问题; 2,民营企业尤其是偏传统行业的企业,如何在新的行业…

基于SSM的高校图书借阅管理系统

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用JSP技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目&#x…