多线程(二):Thread类常见的属性和方法

news2024/12/23 5:42:14

目录

1、run & start

 2、Thread类常见的属性和方法

2.1 构造方法

2.2 属性

3、后台进程 & 前台进程

4、setDaemon

5、isAlive

6、终止一个线程

6.1 变量捕获

6.2 currentThread & isInterrupted & interrupt


1、run & start

在多线程(一)的博客中,为大家介绍了五种创建线程的方式,但从大类上分也就是两大类——分别是通过自定义类继承Thread以及自定义类实现Runnable接口(解耦合)来实现。

这两种方式中,都用到了start方法以及run方法,这里再次强调一下两种方法的作用及区别。

  • start方法的作用是真正在系统中创建线程并启动线程(由JVM调用操作系统的api完成线程创建操作)。
  • start是jvm提供的方法,本质上是调用操作系统提供的api。
  • start是native修饰的本地方法,说明是在JVM内部实现的,并由C++代码实现(JVM由C++实现)
  •  每个Thread对象,都只能start一次,也就是说一个Thread对象只能创建一个线程。每想创建一个新的线程,就需要创建一个新的Thread对象。(“日抛”,一次性的)
  • run方法是线程的入口方法,不需要手动调用,当新的线程启动后就会自动调用。
  • run方法相当于一个“回调函数”。什么是“回调函数”?简单来说,就是这个方法自己不用,让别人去用。举个例子:我们使用优先级队列(堆)时,需要指定比较规则,其中Comparable的compareTo方法以及Comparator的compare方法就是回调函数。

 2、Thread类常见的属性和方法

2.1 构造方法

相信大家对于前两个构造方法并不陌生,就是我们上文所提到创建线程的两种方法。

而第三种和第四种,则是额外给创建的线程自定义名字。

给线程起名字,其作用就是方便我们程序员调试(通过名字描述线程的作用),哪怕线程的名字相同也不会影响线程的执行。

如下图所示:

我们就可以观察到我们自定义的两个名为t1、t2的两个线程正在运行。 

但是,我们并没有发现主线程main,这是为什么呢?

——这是因为主线程中已经结束了(main中并没有死循环代码,主线程main早已执行完毕了~~)。

我们之前的认知时,main是程序的入口,main执行完程序(进程)就执行完毕了,其实这只是针对于单线程程序来说的。在多线程中,非也~~

我们当然也可以通过写死循环代码来观察主线程main。

public class Demo6 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true) {
                System.out.println("hello t1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "t1");
        t1.start();

        Thread t2 = new Thread(() -> {
            while (true) {
                System.out.println("hello t2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "t2");
        t2.start();

        while (true) {
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
}

2.2 属性

  • ID就类似于pid,是线程的唯⼀标识,不同线程不会重复。
  • 名称,即我们给线程自定义的名字,没有自定义则为系统默认的名字。
  • 状态,优先级这里就不再赘述。

线程中含有多个属性,我们可以通过Thread中相应的方法进行获取。 


3、后台进程 & 前台进程

isDaemon方法用来判断线程是否为 后台线程。

我们需要先了解 前台线程 与 后台线程。

前台线程 的作用大,能够直接影响进程是否执行完毕。当所有的前台线程都执行完后,进程才能结束,要是有一个前台线程没有结束,整个进程都不能结束。

我们自己创建的线程,包括main主线程,都是前台线程。

后台线程,又称为 守护线程,Deamon就是守护的意思。说明这个线程 是默默守护的,他们的存在,不会影响 进程 的结束。

一些JVM自带的线程,就是后台线程,他们的存在左右不了进程的结束。简单来说,如果进程要结束,不会问这些后台线程的意见,进程要是想结束,那他们就必须跟着结束,进程结束后,他们也会随之结束。

有些后台线程存在的意义不是很大,即使没有他们,也没啥影响。但是,有些后台线程是JVM提供的一些具有特殊功能的线程,会跟随整个进程持续执行(不能随便离开),比如:垃圾回收进程。

前台线程 和 后台线程 都是有多个的。

如果有多个前台线程,必须所有的 前台线程 结束后,进程才能结束。

4、setDaemon

我们可以通过setDaemon方法将 前台线程 修改为 后台线程。

注意:必须在线程创建前修改(即调用start方法前使用setDaemon方法修改)

将 前台线程 修改为 后台线程 后,这些线程将无力阻止进程的结束。

此时,仅有一个前台线程main,当main结束后,整个进程将会结束,虽然t1处于循环状态,但t1为后台线程,不会影响整个进程的状态。

  • 进程间存在 父子关系 ,但线程间不存在。

比如 IDEA 本身就是一个进程,打开 IDEA 后运行的Java代码又是一个进程,这就是进程间的父子关系。 

5、isAlive

isAlive方法用来判断线程是否存活。

在Java中,Thread创建的对象和线程是一一对应的关系。

但是Thread对象的生命周期和所创建的线程的生命周期是不同的,可能存在线程已经结束,但是Thread对象依旧存活的情况。

我们将线程设置为存活3s,但是发现却打印出了4个true,这是由于并发时操作系统随机调度的原因,主线程的第四次打印,和thread线程结束,谁先谁后,是不一定的~~

在这里就是,在主线程第四次打印true时(主线程先被调度执行),thread线程还有一口气,但是马上就无了~~


6、终止一个线程

"终止" 指的是让一个线程结束,且不会再恢复了。

当一个线程的入口方法执行完毕后,线程也就随即结束了。

那么让一个线程提前终止,其实就是让run方法尽早return而已。

6.1 变量捕获

我们可以通过变量设置,控制线程的存活时间。

此时可以得到我们想要的结果。

此时的isFinish为成员变量,但是当我们把isFinish放到main方法中当做局部变量时,却发生了报错,这是为什么呢?

它告诉我们,isFinish应该是final类型或者事实上为final类型的,也就是说isFinish的值不能发生改变。

这是因为,在lambda内部,触发了“变量捕获”的语法,而我们在下面的代码中对isFinish进行了修改,所以发生了报错。

因为lambda本来就是用于“回调函数”使用的,而 回调函数 执行时机是不确定的,可能是很久以后才进行线程的创建,而此时main这个主线程可能早已经执行完毕了,那么main内部的局部变量早已销毁。

为解决上述问题,Java将被捕获的变量拷贝了一份,传到了lambda的内部,以此让lambda能够使用外面的局部变量。但是,拷贝的变量,就意味着这个变量和原本的变量不是同一个东西,当一方进行修改时,另一方不会随之改变。而这边变,那边又不变,就会给程序猿带来更多的问题~~

为解决这个问题,Java大佬们这样决定,就根本不允许这个变量进行修改。这就是“变量捕获”的语法,被捕获的变量必须是final或事实上为final的(不是final类型,但是变量值没有改变)。

而当我们将isFinish设置为成员变量时,此时就不再是“变量捕获”语法的范畴,而切换为“内部类访问外部类成员”的语法(lambda本来就是实现函数式接口的匿名内部类的简化形式,本质上就是一个内部类),而“内部类访问外部类成员”的语法本来就是正确的。

对象本体以及其中的成员变量的生命周期都是由GC(垃圾回收)进行管理的,不会随方法的结束而销毁。

当final修饰引用类型时,不能修改引用的指向(不能将这个引用指向别的对象),但是可以修改引用指向的对象本体。

6.2 currentThread & isInterrupted & interrupt

  • isInterrupted方法的作用就是判断线程是否被终止。
 

lambda表达式的执行是在对象new之前,此时的thread引用还没有被声明。

我们需要使用Thread的静态方法currentThread获取线程的引用。

  • currentThread方法在哪个线程内部被调用,获取的就是哪个线程的Thread引用。而这个代码是在 lambda 中 (也就是在 thread 线程的入口run方法中) 调用的,获取的就是 thread 线程的引用。(类似于this)

故 Thread.currentThread().isInterrupted() 的作用就是,判断线程是否被终止了。

  • interrupt方法的作用是去主动终止线程(修改内部的boolean变量(标志位)值)
 

当我们设置好终止线程的方法后,运行发现有异常抛出,被JVM捕获,导致程序直接终止:

这是由于调用interrupt方法的原因:

  1. interrupt方法不仅会修改线程内部的boolean变量值
  2. 还会唤醒类似于sleep这样的阻塞方法,使其抛出异常,若catch块内没有将异常处理好,则会直接带走整个进程

而我们不想让线程"掀桌",我们可以修改 IDEA 默认的处理异常的方式,使用 break 替换,使线程更加优雅的终止:

(虽说是优雅了,但是依然是interrupt唤醒了sleep,抛出异常,通过catch逻辑来进行的线程终止操作)

public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(() -> {
            //这个代码是在 lambda 中 (也就是在 thread 线程的入口run方法中) 调用的,获取的就是 thread 线程的引用
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    System.out.println("hello thread");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // 唤醒sleep,抛出异常,catch捕获后再次抛出新异常
                    // 线程的 “掀桌” 操作
                    //throw new RuntimeException(e);
                    break;
                }
            }
            System.out.println("thread 结束");
        }, "thread");
        thread.start();

        Thread.sleep(3000);
        // 主动终止线程
        System.out.println("main 尝试终止线程thread");
        thread.interrupt();

    }

END

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

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

相关文章

Windows环境mysql 9安装mysqld install报错:Install/Remove of the Service Denied!

Windows环境mysql 9安装mysqld install报错:Install/Remove of the Service Denied! 解决方案: 控制台/批处理命令窗口需要以系统管理员身份运行。 mysql数据库环境配置和安装启动,Windows-CSDN博客文章浏览阅读920次。先下载mysql的zip压缩…

opencv的相机标定与姿态解算

首先我们要知道四个重要的坐标系 世界坐标系相机坐标系图像成像坐标系图像像素坐标系 坐标系之间的转换 世界坐标系——相机坐标系 从世界坐标系到相机坐标系,涉及到旋转和平移(其实所有的运动也可以用旋转矩阵和平移向量来描述)。绕着不…

Android开发视频预览效果

Android开发视频预览效果 视频播放不是一个简单的事情,得有暂停,继续播放等功能,屏幕的适配也是头疼的事情 一、思路: 引用的是腾讯播放器TXVodPlayer 二、效果图: 图片不是很直观,也可以看下视频 And…

【unity进阶知识12】从零手搓unity存档存储数据持久化系统,实现对存档的创建,获取,保存,加载,删除,缓存,加密,支持多存档

文章目录 前言一、Unity对Json数据的操作方法一、JsonUtility方法二、Newtonsoft 二、持久化的数据路径三、数据加密/解密加密方法解密方法 四、条件编译指令限制仅在编辑器模式下进行加密/解密四、数据持久化管理器1、存档工具类2、一个存档数据3、存档系统数据类4、数据存档存…

【STM32单片机_(HAL库)】4-5-2【定时器TIM】【感应开关盖垃圾桶项目】HC-SR04超声波模块实验

1.硬件 STM32单片机最小系统HC-SR04超声波模块 2.软件 hcsr04驱动文件添加main.c程序 #include "sys.h" #include "delay.h" #include "led.h" #include "uart1.h" #include "hcsr04.h"int main(void) {HAL_Init(); …

OceanBase管理着工具-oat安装

https://www.oceanbase.com/softwarecenter-enterprise https://www.oceanbase.com/docs/enterprise-oat-doc-cn-1000000000762607 (base) [rootlnpg soft]# pwd /db/ob/soft (base) [rootlnpg soft]# ll 总用量 4274536 -rw-r--r-- 1 root root 1730447360 6月 26 14:06 oa…

『网络游戏』客户端发送消息到服务器【17】

将上一章服务器的协议PEProtocol的.dll文件重新生成导入unity客户端中 命名为Net 点击生成 另一种导入.dll文件方式 在客户端粘贴即可 此时Net文件夹的.dll文件就导入进来了 创建脚本:NetSvc.cs 编写脚本:NetSvc.cs 修改脚本:GameRoot.cs 在…

Spring源码:SpringBoot启动流程分析

目录 一、演示代码二、功能介绍三、代码分析1、从主启动类中调用run()方法出发2、看一下SpringApplication的构造方法在干什么?3、看下run()方法的主要流程代码4、run():启动计时器计算springboot启动时间5、run():创建DefaultBootstrapConte…

基于单片机的山林远程环境监测仪设计

本设计基于单片机的智能化的远程山林环境检测仪,该检测仪由硬件系统和软件系统构成。电源管理模块给整个硬件系统提供工作所需电源,系统可完成山林环境有关的温度、湿度、火焰和海拔高度的采集,并且可通过与按键设置阈值作对比判断危险情况&a…

C++ | Leetcode C++题解之第470题用Rand7()实现Rand10()

题目&#xff1a; 题解&#xff1a; class Solution { public:int rand10() {int a, b, idx;while (true) {a rand7();b rand7();idx b (a - 1) * 7;if (idx < 40) {return 1 (idx - 1) % 10;}a idx - 40;b rand7();// get uniform dist from 1 - 63idx b (a - 1)…

Halcon OCR字符识别

create_text_model_reader创建一个文本模型描述要用于分割的文本find_text. 的parameter value文本分段方法的值为自动和手动. 通常&#xff0c;参数模式应设置为自动因为这种模式更稳定&#xff0c;需要更少 配置工作量。请注意&#xff0c;在这种情况下&#xff0c;还有一个…

Redis Windows最新安装教程(2024.10.10)

文章目录 redis介绍下载地址 安装流程基础操作测试Redis常用的服务指令 redis介绍 Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源的、基于内存的数据结构存储系统&#xff0c;常用作数据库、缓存和消息中间件。Redis具有快速、灵活、可扩展和高可用性等特…

2024最新会声会影序列号及会声会影2023怎样添加画中画滤镜

深入简单直观的视频编辑&#xff01;使用 Corel VideoStudio会声会影2023&#xff0c;将您最美好的时刻和生活体验变成令人惊叹的电影&#xff0c;这是一款有趣且直观的视频编辑器&#xff0c;包含高级工具和高级效果会声会影2024免费下载。从自定义标题和过渡&#xff0c;到 M…

微服务(二)

目录 一、服务注册和发现 1、注册中心原理 2、nacos注册中心 3、服务注册 3.1 添加依赖 3.2 配置nacos 3.3 服务的发现 二、openfeign 1、快速入门 1.1 引入依赖 1.2 启用openfeign 1.3 编写openfeign客户端 2、连接池 2.1 引入依赖 2.2 开启连接池 3、 最佳实…

用godot4.3 C#开发一个APP登陆界面

一、下载godot godot 是一个游戏开发平台&#xff0c;可以用gd语言&#xff0c;这一个类似python,python程序员转gd很容易&#xff0c;也可以用C#开发。 https://godotengine.org/ 进行下载&#xff0c;下载 下载,net版本。当然mac也可以下载&#xff0c;linux上也有可以&#…

[含文档+PPT+源码等]精品基于Python实现的Django高校职业通的设计与实现

基于Python实现的Django高校招聘系统的设计与实现背景&#xff0c;主要源于以下几个方面的需求和趋势&#xff1a; 1. 就业压力的增加 随着高校毕业生数量的不断增加&#xff0c;就业压力日益加大。传统的招聘方式&#xff0c;如招聘会、报纸广告等&#xff0c;由于其信息传播…

C语言 | Leetcode C语言题解之第470题用Rand7()实现Rand10()

题目&#xff1a; 题解&#xff1a; // The rand7() API is already defined for you. // int rand7(); // return a random integer in the range 1 to 7int rand10() {while(true) {int index (rand7() - 1) * 7 rand7(); if(index < 40) return index % 10 1; } }

ThingsBoard规则链:Copy Keys节点详解

引言 复制键节点简介 用法 含义 应用场景 实际项目运用示例 智能农业监控系统 城市交通管理系统 结论 引言 ThingsBoard是一个功能丰富的物联网平台&#xff0c;它支持设备管理、数据收集与处理以及实时监控。其强大的规则引擎允许用户通过创建复杂的业务逻辑来自动处理…

探索高效的 PDF 拆分工具及其独特功能

当一份大型的PDF文档包含了多个不同主题或章节的内容时&#xff0c;将其拆分成独立的部分可以更方便我们的阅读、编辑和管理。接下来&#xff0c;让我们一起走进PDF拆分工具的世界&#xff0c;了解它们的功能和价值。 1.福昕PDF编辑器 链接一下>>https://editor.foxits…

6-基于TMS320C6678、FPGA XC5VSX95T的6U CPCI 8路光纤信号处理卡

1、板卡概述   本板卡由我公司自主研发&#xff0c;基于CPCI架构&#xff0c;符合CPCI2.0标准&#xff0c;采用两片TI DSP TMS320C6678芯片和Xilinx公司V5系列FPGA XC5VSX95T-1FF1136芯片。包含PCI接口、GMII的以太网接口、Nor Flash接口、8路SFP光 纤&#xff0c;4路RS232。…