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

目录

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.mzph.cn/news/881450.shtml

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

相关文章

Java面试宝典-Java集合01

Java面试宝典-Java集合01 目录 Java面试宝典-Java集合01 1、Java中常用的集合有哪些? 2、Collection 和 Collections 有什么区别? 3、为什么集合类没有实现 Cloneable 和 Serializable 接口? 4、数组和集合有什么本质区别? 5、数组…

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

题目&#xff1a; 题解&#xff1a; class Solution extends SolBase {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 - 63…

蓝桥杯【物联网】零基础到国奖之路:十七. 扩展模块之单路ADC和NE555

蓝桥杯【物联网】零基础到国奖之路:十七. 扩展模块之单路ADC和NE555 第一节 硬件解读第二节 CubeMx配置第三节 代码1&#xff0c;脉冲部分代码2&#xff0c;ADC部分代码![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/57531a4ee76d46daa227ae0a52993191.png) 第一节 …

React技术在Meta Connect 2024大会

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

熵权法计算评价指标权重——使用Excel VBA实现

[ 熵权法 ] 信息是系统有序程度的一个度量&#xff0c;熵是系统无序程度的一个度量&#xff1b;根据信息熵的定义&#xff0c;对于某项指标&#xff0c;可以用熵值来判断某个指标的离散程度&#xff0c;其信息熵值越小&#xff0c;指标的离散程度越大&#xff0c; 该指标对综合…

数据库——表格之间的关系(表格之间的连接和处理)

数据库表格之间经常存在各种关系&#xff1a; 一对一、一对多、多对多 1.一对一 —— 丈夫表&#xff0c;妻子表为例 连接方式一&#xff1a;合并为一张表 这种方式对于一对一来说最优 连接方式二&#xff1a;在其中一张表内加入一个外键&#xff0c;连接另一张表 连…

ARM base instruction -- sdiv

有符号除法运算 Signed Divide divides a signed integer register value by another signed integer register value, and writes the result to the destination register. The condition flags are not affected. 将一个有符号整数寄存器值除以另一个有符号整数寄存器值&am…

Java中的switch分支结构

switch分支结构 switch分支结构1.基本语法2.说明3.流程图4.案例5.注意事项6.练习7.switch和if的比较 switch分支结构 1.基本语法 switch&#xff08;表达式&#xff09;{case 常量1: //当...语句块1;break;case 常量2: 语句块2;break;...case 常量n: 语句块n;break;defaul…

路径跟踪之导航向量场——二维导航向量场

今天带来一期轨迹跟踪算法的讲解&#xff0c;首先讲解二维平面中的导航向量场[1]。该方法具有轻量化、计算简便、收敛性强等多项优点。该方法根据期望的轨迹函数&#xff0c;计算全局位置的期望飞行向量&#xff0c;将期望飞行向量转为偏光角&#xff0c;输入底层控制器&#x…

prometheus client_java实现进程的CPU、内存、IO、流量的可观测

文章目录 1、获取进程信息的方法1.1、通过读取/proc目录获取进程相关信息1.2、通过Linux命令获取进程信息1.2.1、top&#xff08;CPU/内存&#xff09;命令1.2.2、iotop&#xff08;磁盘IO&#xff09;命令1.2.3、nethogs&#xff08;流量&#xff09;命令 2、使用prometheus c…

AAA Mysql与redis的主从复制原理

一 &#xff1a;Mysql主从复制 重要的两个日志文件&#xff1a;bin log 和 relay log bin log&#xff1a;二进制日志&#xff08;binnary log&#xff09;以事件形式记录了对MySQL数据库执行更改的所有操作。 relay log&#xff1a;用来保存从节点I/O线程接受的bin log日志…

用凡尔码系统进行隐患排查二维码的制作

隐患排查是企业安全管理的重要环节&#xff0c;通过定期或不定期地对生产设备、作业场所、作业人员等进行检查&#xff0c;发现并消除安全隐患&#xff0c;预防事故的发生。隐患排查的效率和质量直接影响到企业的安全生产水平和经济效益。 传统的隐患排查方法主要依靠纸质进行…

PostgreSQL学习笔记七:常规SQL操作

PostgreSQL 支持标准的 SQL 语句&#xff0c;同时也扩展了一些特有的功能。以下是一些常规的 SQL 语句示例&#xff0c;这些示例涵盖了数据定义、数据操作和数据查询的基本操作&#xff1a; 数据定义语言 (DDL 创建数据库&#xff1a; CREATE DATABASE mydatabase;创建表&#…

Vue/组件的生命周期

这篇文章借鉴了coderwhy大佬的Vue生命周期 在Vue实例化或者创建组件的过程中 内部涉及到一系列复杂的阶段 每一个阶段的前后时机都可能对应一个钩子函数 以下是我根据coderwhy大佬文章对于每一个阶段的一些看法 1.过程一 首先实例化Vue或者组件 在实例化之前 会对应一个钩子函…

安装最新 MySQL 8.0 数据库(教学用)

安装 MySQL 8.0 数据库&#xff08;教学用&#xff09; 文章目录 安装 MySQL 8.0 数据库&#xff08;教学用&#xff09;前言MySQL历史一、第一步二、下载三、安装四、使用五、语法总结 前言 根据 DB-Engines 网站的数据库流行度排名&#xff08;2024年&#xff09;&#xff0…

使用 Go 语言与 Redis 构建高效缓存与消息队列系统

什么是 Redis&#xff1f; Redis 是一个开源的内存数据库&#xff0c;支持多种数据结构&#xff0c;包括字符串、列表、集合、哈希和有序集合。由于 Redis 运行在内存中&#xff0c;读写速度极快&#xff0c;常被用于构建缓存系统、实时排行榜、会话存储和消息队列等高并发场景…

【浏览器】如何正确使用Microsoft Edge

1、清理主页广告 如今的Microsoft Edge 浏览器 主页太乱了&#xff0c;各种广告推送&#xff0c;点右上角⚙️设置&#xff0c;把快速链接、网站导航、信息提要、背景等全部关闭。这样你就能得到一个超级清爽的主页。 网站导航       关闭 …

十款文件防泄密软件推荐,保护您的重要信息

信息安全是现代社会不可忽视的重要话题&#xff0c;尤其是在工作和生活中接触到大量敏感数据时。选择合适的文件防泄密软件&#xff0c;可以有效防止信息泄露。以下是我们为您推荐的十款优秀软件。 Ping32 以高效的文件加密功能而闻名&#xff0c;Ping32 可以轻松保护您的文件&…

【JavaEE】——回显服务器的实现

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 一&#xff1a;引入 1&#xff1a;基本概念 二&#xff1a;UDP socket API使用 1&#xff1a;socke…

笔记||VUE3

侦听器 | Vue.js (vuejs.org) 模板引用 | Vue.js (vuejs.org)