JavaEE初阶Day 4:多线程(2)

news2024/11/15 19:28:07

目录

  • Day4:多线程(2)
    • 1. catch语句
    • 2. sleep的处理
    • 3. Thread
      • 3.1 Thread构造方法
      • 3.2 Thread的属性
        • 3.2.1 ID
        • 3.2.2 优先级
        • 3.2.3 后台线程
        • 3.2.4 存活
        • 3.2.5 start
        • 3.2.6 中断
          • 3.2.6.1 控制线程结束代码
          • 3.2.6.2 interrupt和isInterrupted

Day4:多线程(2)

1. catch语句

catch语句中有两种代码

  • throw new RuntimeException(e);:继续再抛出新的异常
  • e.printStackTrace();:打印异常调用栈

第一个是新版idea自动生成的,第二个是老版idea自动生成的,当前阶段使用哪个写法都可以,没有太大区别

但是catch中应该写什么,在实际开发中,是应该自定义的,都是规划好的,如下:

​ 1)可能会打印一些日志,把出现异常的详情都记录到日志文件中
​ 2)触发一些重试类的操作
​ 3)触发一些"回滚"类的操作
​ 4)触发一些报警机制(而是给程序员发短信/微信/打电话 告诉程序员你程序出问题了)

try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    throw new RuntimeException(e);
}
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

2. sleep的处理

在main方法中处理sleep,可以throws,也可以try catch,但是在线程的run中就只有一个选择了,只能try catch

  • throws也是方法签名的一部分,方法签名包含了方法名字、方法的参数列表、声明抛出的异常,不包含返回值、public/private,在方法重写的时候,要求方法签名是一样的,然而父类的run方法并没有抛出异常,所以重写的时候,就无法抛出异常了

3. Thread

3.1 Thread构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable对象创建线程对象
Thread(String name)创建线程对象,并命名
Thread(Runnable target, String name)使用Runnnable对象创建线程对象,并命名
Thread(ThreadGroup group, Runnable target)【了解】线程可以被用来分组管理
  • Thread(String name):可以在创建线程的时候,给线程起个名字,是否起名字,对于线程本身的运行效果,是没有任何影响的,但是起名字有个好处,Java进程运行过程中,可以通过工具(jconsole/IDEA)看到每个不同线程的名字,出现问题的时候,更直观的把问题的线程和代码关联起来(方便调试)
  • Thread(ThreadGroup group, Runnable target):开发中很少用到,有的时候,希望把多个线程进行分组,分组之后,就可以针对不同的组批量进行控制,这种写法目前实际开发中更多的是被线程池取代了
package thread;

public class Dmeo6 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            
            while (true){

                System.out.println("hello");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            
        },"自定义线程");
        
        
        t.start();
        //至此main方法结束,意味着main线程就结束了(销毁了)
    }
}

在这里插入图片描述

这里没有看到main线程,一个进程启动,肯定得先有main线程调用main方法。注意,此处不是main线程没有被创建,而是执行太快,执行完毕了!main线程是JVM通过C++代码创建出来的,没有通过Java中的Thread类创建,就不会重写run

3.2 Thread的属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()
3.2.1 ID

这里的ID和系统中PCB上的ID是不同的,是JVM自己搞得一套ID体系,Java代码无法获取到PCB的ID,虽然是一一对应的,但是编号不是一个体系

3.2.2 优先级

虽然Java提供了优先级接口,实际上就算修改了优先级,现象也不明显,自己修改了优先级是一回事,系统调度又是另一回事,这里的优先级只能是一个建议参考,具体还是要以系统自身为准,通过C调用系统原生API修改某个PCB里的优先级也是没有明显现象的

3.2.3 后台线程
  • 前台线程指的是,这样的线程如果不运行结束的话,Java进程是一定不会结束的;后台进程指的是,这样的线程,即使继续在执行,也不能阻止Java进程结束,前台线程可以有多个,直到最后一个前台线程结束,进程才会结束
  • 在Java代码中,main线程就是前台线程,程序员创建出来的线程,默认情况下都是前台线程,可以通过setDaemon方法来把线程设置为后台线程,在jconsole中看到的JVM中包含一些其他的内置的线程,就属于后台线程了,比如有的线程负责进行gc(垃圾回收),gc是要有周期性持续性执行的,不可能主动结束,要是把他设为前台,进程就永远都结束不了了
package thread;

public class Demo7 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        //设置为后台线程,必须设置在start之前,开弓没有回头箭
        t.setDaemon(true);

        t.start();
        //此时进程中只有main是前台线程了,只要main结束,整个进程就结束了,main执行完start立即结束了,此时t还没来得及打印,进程结束了,里面的线程自然随之结束
    }
}

注意:此处也是有一定概率出现t打印一次,然后结束进程的情况,这个事情就看是main先执行结束,还是t先执行一次打印(线程之间是抢占式执行,调度顺序不确定)

3.2.4 存活

指的是系统中的线程(PCB)是否还存在,Thread对象的生命周期和PCB的生命周期是不一定完全一样的

package thread;

public class Demo8 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        t.start();
        Thread.sleep(1000);
        System.out.println(t.isAlive());//true
    }
}

上述写法会导致县城还没有执行完毕,但是t指向的对象就要被GC回收了

3.2.5 start

start真正创建线程(在内核中创建PCB)

  • 一个线程需要先通过run/lambda把线程要完成的任务,定义出来,start才是真正创建线程,并开始执行
  • 核心就是是否真的创建线程出来,每个线程都是独立调度执行的,相当于整个程序中多了一个执行流
  • 一个Thread对象,只能start一次,要想再搞另一个线程,就需要创建另一个Thread对象
package thread;

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

        Thread t2 = new Thread(()->{
            System.out.println("hello t2");
        });

        t.start();
        t2.start();
    }
}
3.2.6 中断

终止线程,在Java中,都只是“提倡、建议”,真正要不要终止,还得线程本体来进行决定

t线程正在执行,其他线程,只能提醒一下t是不是要终止了,t收到这样的提醒之后,也还是得自己决定的

系统原生的线程中,其实有办法让别的线程被强制终止的,这种设定,其实不太好,所以Java没有采纳过来

线程之间调度是随机的,万一线程正在做一个很重要的工作,干了一半,强制结束可能引起一些bug

3.2.6.1 控制线程结束代码

核心思路:让需要终止的线程的入口方法尽快执行结束(跳出循环,还是尽快return都无所谓)

package thread;

public class Demo10 {
    
    private static boolean isRunning = true;
    public static void main(String[] args) {
        
        Thread t = new Thread(()->{
            // boolean isRunning = true;

            while (isRunning){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("t线程结束了");
        });
        
        t.start();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        
        //3s后,主线程修改isRunning的值,从而通知t结束
        System.out.println("控制t线程结束");
        isRunning = false;
    }
}

如果是// boolean isRunning = true;会出现编译出错

变量捕获:作为lambda或者匿名内部类,都能捕获到外面一层作用域中的变量名,就可以使用,变量捕获有一个前置条件,就是要求得是final或者事实final,即没有人修改其变量的值

然而使用private static boolean isRunning = true;:原理是内部类访问外部类的成员,lambda本质上是一个匿名内部类,实现了函数式接口

3.2.6.2 interrupt和isInterrupted
package thread;

public class Demo11 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            while (!Thread.currentThread().isInterrupted()){
                System.out.println("hello thread");
                try {
                    Thread.sleep(10000);//10s,但是会唤醒sleep抛出异常
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        t.interrupt();
    }
}

Thread.currentThread().isInterrupted()

  • currentThread():是一个静态方法,这个方法能够获取到当前线程,获取到t这个引用
  • isInterrupted():线程内置的标志位,boolean变量,true表示线程要终止了,false表示线程要继续执行

t.interrupt();

  • 通过这个方法,就相当于是设置boolean值为true
  • 除了能设置boolean值,还可以唤醒sleep等阻塞方法,即使正在sleep(10s),刚休眠1s
    • 第一种写法,必须等待9s,才能让线程结束(sleep结束了,才能继续进行循环判定)
    • 第二种写法,则立即就会让sleep抛出一个InterruptedException异常,不会再等待,立即就唤醒了

当使用Interrupt方法之后,此时要不要结束,都是t线程自己决定的

当进行了t.interrupt();之后,修改了标志位,但是由于sleep的存在,如果代码没有sleep,确实是直接修改了标志位就结束了,但是有sleep的时候,触发Interrupt的时候,线程正在sleep,sleep被唤醒(进入catch中)的同时,就会清除刚才的标志位(又改回false),之所以这样做是为了要让程序员自己决定要不要结束,是继续执行,还是要立即结束,还是要等会结束

//1.程序员认为继续执行
while (!Thread.currentThread().isInterrupted()){
    System.out.println("hello thread");
    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {

    }
}

//2.立即结束
while (!Thread.currentThread().isInterrupted()){
    System.out.println("hello thread");
    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        break;
    }
}

//3.等会结束
while (!Thread.currentThread().isInterrupted()){
    System.out.println("hello thread");
    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        //写一些逻辑之后,再break
        break;
    }
}

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

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

相关文章

学习笔记——微信小程序读取当前时间

<view class"box"><text>日期:</text><view class"date">{{obtaindate}}</view></view> wxml中定义了一个文本元素&#xff0c;通过{{obtaindate}}获取js页面传递的日期数据 data:{obtaindate:"" }, onlo…

公链角逐中突围,Solana 何以成为 Web3 世界的流量焦点?

在众多区块链公链中&#xff0c;Solana 凭借其创纪录的处理速度和极低的交易费用&#xff0c;成为了众多开发者和投资者的宠儿。就像网络上流行的那句话所说&#xff1a;“Why slow, when you can Solana?”&#xff0c;Solana 正以它的速度和强大的生态系统&#xff0c;重新定…

nacos的各种类型的配置文件 yml 、json、 Properties、 text 等文件类型 发生变化怎么热更新,实现实时监听nacos配置文件变化

本文用的是 Nacos作为配置中心注册监听器方法 实现热更新 nacos 配置文件 从而不用重启项目 依赖、工具类 这边就不写了 因为项目用的是 Json 类型的配置文件 所以下文 主要是对json文件进行实现 别的文件大同小异 先说扯淡的东西 在nacos 的配置文件中 dataId 这两种声明 是…

Postman传对象失败解决

文章目录 情景复现解决方案总结 情景复现 postman中调用 debug发现pId传入失败 分析解释&#xff1a; 实体类中存在pId、uid和num字段 controller层将GoodsCar作为请求体传入 解决方案 当时觉得很奇怪&#xff0c;因为uid和num可以被接收&#xff0c;而pId和num的数据类型相…

图腾柱PFC:HP1010为您的电动两轮车之旅提供绿色,高效,安全的动力

电动两轮车不仅为当今生活提供了便利&#xff0c;更是一种健康和绿色的出行方式。想象一下&#xff0c;在经过一整晚的充分休息&#xff0c;骑上爱车&#xff0c;满血复活的准备开始新的一天。您会愿意带着如何给心爱的两轮车充电的担心开始这一天吗&#xff1f; 随着越来越…

HackTheBox-Machines--Legacy

文章目录 1 端口扫描2 测试思路3 445端口漏洞测试4 flag Legacy 测试过程 1 端口扫描 nmap -sC -sV 10.129.227.1812 测试思路 目标开启了135、139、445端口&#xff0c;445 SMB服务存在很多可利用漏洞&#xff0c;所以测试点先从445端口开始。而且在Nmap扫描结果中&#xff0c…

Unity 窗口化设置

在Unity中要实现窗口化&#xff0c;具体设置如下&#xff1a; 在编辑器中&#xff0c;选择File -> Build Settings。在Player Settings中&#xff0c;找到Resolution and Presentation部分。取消勾选"Fullscreen Mode"&#xff0c;并选择"Windowed"。设…

Unity2018发布安卓报错 Exception: Gradle install not valid

Unity2018发布安卓报错 Exception: Gradle install not valid Exception: Gradle install not valid UnityEditor.Android.GradleWrapper.Run (System.String workingdir, System.String task, System.Action1[T] progress) (at <c67d1645d7ce4b76823a39080b82c1d1>:0) …

通用指南-营销和设计中的增强现实(AR)

原文作者&#xff1a;Superside 翻译&#xff1a;数字化营销工兵 --- 经典万字长文&#xff0c;权威解读&#xff0c;分享经典&#xff0c;预计阅读完需要30分钟&#xff0c;建议收藏&#xff01; 目录 一、引言 为什么要尝试AR AR到底是什么&#xff1f;营销人员和创意人…

网络工程师实验命令(华为数通HCIA)

VRP系统的基本操作 dis version #查看设备版本信息 sys #进入系统视图 system-name R1 #改设备名字为R1进入接口配置IP地址 int g0/0/0 ip address 192.168.1.1 255.255.255.0 #配置接口地址为192.168.1.1/255.255.255.0 ip address 192.168.1.2 24 sub #此…

Intellij IDEA安装配置Spark与运行

目录 Scala配置教程 配置Spark运行环境 编写Spark程序 1、包和导入 2、定义对象 3、主函数 4、创建Spark配置和上下文 5、定义输入文件路径 6、单词计数逻辑 7、输出结果 8、完整代码&#xff1a; Scala配置教程 IDEA配置Scala&#xff1a;教程 配置Spark运行环境 …

持续集成流程主要系统构成介绍(CI)

目录 一、概述 二、版本控制系统 2.1 概述 2.2 版本控制系统使用流程示意图 2.3 版本控制软件划分 2.3.1 集中式版本控制软件 2.3.2 分布式版本控制软件 2.3.3 总结 2.4 常用版本控制软件介绍 三、编译构建系统 3.1 概述 3.2 编译构建流程示意图 3.3 列举Java 源码…

uniApp使用XR-Frame创建3D场景(5)材质贴图的运用

上一篇讲解了如何在uniApp中创建xr-frame子组件并创建简单的3D场景。 这篇我们讲解在xr-frame中如何给几何体赋予贴图材质。 先看源码 <xr-scene render-system"alpha:true" bind:ready"handleReady"><xr-node><xr-assets><xr-asse…

向量法求点在直线上的投影

已知直线上两点a、b和直线外一点p&#xff0c;求p在直线ab上的投影点。 根据《计算几何之 点在直线上的投影 代码模板与证明》一文中所述&#xff0c;p的投影点p’就是a x ⃗ \vec x x &#xff08;直线的点向式&#xff09;&#xff0c;所以我们只要求出 x ⃗ \vec x x 就能…

基于单片机的二维码LCD显示控制设计

**单片机设计介绍&#xff0c;基于单片机的二维码LCD显示控制设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的二维码LCD显示控制设计是一个集硬件、软件与通信于一体的综合性项目。此设计的主要目标是实现单片机…

AI新工具 又一个开源大模型DBRX击败GPT3.5;根据音频和图像输入生成会说话、唱歌的动态视频

✨ 1: AniPortrait 腾讯开源&#xff1a;根据音频和图像输入生成会说话、唱歌的动态视频 AniPortrait 是个先进的框架&#xff0c;专门用来生成高质量的、由音频和参考肖像图片驱动的动画。如果你有视频&#xff0c;也可以用来实现面部的再现&#xff08;Face reenactment&am…

Windows 最佳文件管理器:快速、简单、直观、自由 | 开源日报 No.175

files-community/Files Stars: 30.6k License: MIT Files 是为 Windows 构建的最佳文件管理器应用程序。该项目解决了在 Windows 上进行文件管理时的困难。 它具有以下主要功能和优势&#xff1a; 采用直观设计&#xff0c;使浏览文件变得更加简单支持标签、预览和自定义背景…

面试算法-121-完全二叉树的节点个数

题目 给你一棵 完全二叉树 的根节点 root &#xff0c;求出该树的节点个数。 完全二叉树 的定义如下&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#xff0c;并且最下面一层的节点都集中在该层最左边的若干位置…

多源统一视频融合可视指挥调度平台VMS/smarteye系统概述

系统功能 1. 集成了视频监控典型的常用功能&#xff0c;包括录像&#xff08;本地录像、云端录像&#xff08;录像计划、下载计划-无线导出&#xff09;、远程检索回放&#xff09;、实时预览&#xff08;PTZ云台操控、轮播、多屏操控等&#xff09;、地图-轨迹回放、语音对讲…

Convex and Semi-Nonnegative Matrix Factorizations

我们提出了非负矩阵分解&#xff08;NMF&#xff09;主题的几种新变体。考虑形式为X FG^T的因子分解&#xff0c;我们关注的是G被限制为包含非负元素的算法&#xff0c;但允许数据矩阵X具有混合符号&#xff0c;从而扩展了NMF方法的适用范围。我们还考虑了基向量F被约束为数据…