当前位置: 首页 > news >正文

【多线程】认识Thread类及其常用方法

📄前言
本文是对以往多线程学习中 Thread类 的介绍,以及对其中的部分细节问题进行总结。


文章目录

  • 一. 线程的 创建和启动
    • 🍆1. 通过继承 Thread 类创建线程
    • 🍅2. 通过实现 Runnable 接口创建线程
    • 🥦3. 其他方法创建线程(本质上为上面两种写法的变形)
      • 🥑3.1 使用 Thread 的匿名内部类
      • 🥬3.2 使用匿名内部类实现 Runnable 接口
      • 🥒3.3 使用 lambda 表达式实现 Runnable 接口 (推荐,更加简洁!!!)
    • 🍉4. 线程的启动(关于 start方法 和 run方法 的思考)
  • 二. Thread类的属性和常用方法
    • 🍚1. Thread类的主要属性
    • 🍥2. 线程休眠
    • 🍭3. 线程等待
    • 🍦 4. 获取线程实例
    • 🧊5. 线程的中断 (关于线程中断的细节!!!)

之前的文章介绍过线程的引入能够更好地处理程序的并发执行问题。在Java中,线程的创建方式之一是通过 Thead类 (Thead封装了操作系统提供的API,使我们创建的线程能够系统的调度)。接下来我们先了解一下线程的创建方法。

一. 线程的 创建和启动

通过 Thread类 创建线程的方式总体来说可以分为以下两种:

  1. 继承 Thread类,重写类中的 run() 方法
  2. 实例化Thread类,实现 Runnable 接口,重写接口中的 run() 方法(通过Thread的构造方法传参 间接重写run() 方法)

这里我们应该可以发现一个共同点,这两种方法都需要重写 run() 方法,毫无疑问 run() 方法就是线程创建后执行代码逻辑的 “入口”方法,通过查看源码我们发现 Thread类 继承了Runnable,而 run() 方法就是该接口中的抽象方法。(如下)
在这里插入图片描述
在这里插入图片描述

🍆1. 通过继承 Thread 类创建线程

class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println("hello thread!");}System.out.println("线程结束!");}
}

🍅2. 通过实现 Runnable 接口创建线程

在这里插入图片描述

class MyRunnable implements Runnable {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println("hello thread !");}System.out.println("线程结束 !");}
}public class Demo2 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(new MyRunnable());}}

🥦3. 其他方法创建线程(本质上为上面两种写法的变形)

🥑3.1 使用 Thread 的匿名内部类

Thread t = new Thread(){@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println("hello thread !");}System.out.println("线程结束 !");}
};

🥬3.2 使用匿名内部类实现 Runnable 接口

Thread t = new Thread(new Runnable() {@Overridepublic void run() {while(true) {System.out.println("hello thread !");}System.out.println("线程结束 !");}
});

🥒3.3 使用 lambda 表达式实现 Runnable 接口 (推荐,更加简洁!!!)

Thread t = new Thread(() -> {while(true) {System.out.println("hello thread !");}System.out.println("线程结束 !");
});

🍉4. 线程的启动(关于 start方法 和 run方法 的思考)

其实线程的启动方式很简单,直接调用 Thread类中的 start()方法 即可启动线程,为了观察新创建的线程和 “主线程main” 的并发执行效果,代码中调用 Thread类的类方法 sleep(), 让每次语句执行后休眠 1s。(代码及程序运行效果如下)

public static void main(String[] args) throws InterruptedException {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);}}});System.out.println("线程启动 !");t.start();// 主线程for(int i = 0; i < 5; i++) {System.out.println("hello main !");Thread.sleep(1000);}
}

在这里插入图片描述

通过上面代码的执行结果我们容易知道:创建的线程执行的代码其实就是 run() 方法中的代码,那么我们是否可以通过执行 t.run() 来代替 t.start() 呢?
答案很明显是否定的,因为直接调用 run() 方法本质上与一个自定义函数的调用并无任何差异。我们都知道,在程序的某处调用一个函数,程序会在该函数执行结束后才继续执行后面的代码,因此不能使用 t.run() 来代替 t.start()。
总结:调用run() 方法只是一次简单的函数调用,程序依旧是顺序执行;只有调用 start() 才能在操作系统真正创建一个主线程并发执行的线程


二. Thread类的属性和常用方法

🍚1. Thread类的主要属性

在这里插入图片描述
注意:所有创建的线程默认为前台线程,只有当所有前台线程运行结束后,程序才会结束,主线程main运行结束,不会影响其他前台线程的运行。(可以通过setDaemon()方法将线程设置为后台线程)

🍥2. 线程休眠

在这里插入图片描述
代码示例及程序运行结果如下:让新线程每1s进行一次打印,主线程每2s进行一次打印(注意:使用sleep()方法需要处理可能抛出的异常)

public static void main(String[] args) {Thread t = new Thread(() -> {for (int i = 0; i < 6; i++) {System.out.println("hello thread !");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t.start();for (int i = 0; i < 3; i++) {System.out.println("hello main !");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}
}

在这里插入图片描述

🍭3. 线程等待

在这里插入图片描述
代码实例和程序运行结果如下:让主线程进行一次打印后,等待新线程结束再继续运行。

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

在这里插入图片描述

🍦 4. 获取线程实例

在线程的创建过程中,由于 Thread类 还没有被实例化,因此不能通过类对象的引用变量直接得到该对象,而应该使用 Thread类提供的方法得到该对象的引用。
在这里插入图片描述

🧊5. 线程的中断 (关于线程中断的细节!!!)

在正式了解线程中断的方法之前,我们需要知道一个基本的事实:
Java中线程的中断方式并不是让一个正在运行的线程直接中止,而是以一种 “通知的方式” 告诉线程,“你当前应该结束线程了”,而具体是否立即结束当前该线程,由线程接收通知后的代码处理逻辑决定

==========

关于线程的中断方法,Thread类 提供了以下三个方法:
在这里插入图片描述

Thread类收到通知的方式有以下两种情况:

  1. 当前线程因 sleep/join/wait 等方法引起阻塞而挂起,以 InterruptedException 异常的形式通知,并清除中断标志,此时线程的阻塞状态立即结束,而是否立即结束由 try/catch 中catch的处理逻辑决定。(注意:线程是否有后续的处理权,取决于该线程是否有能引起阻塞状态的代码
  2. 当前线程处于正常运行状态,将中断标志位设置为true。

==========

  1. 当线程处于阻塞状态时,存在以下三种对应的处理方式:
    1)不管这个通知,继续运行。 2)直接结束线程。 3)进行一些特定的收尾工作再结束线程
public static void main(String[] args) {Thread t = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {System.out.println("hello thread !");try {Thread.sleep(1000);} catch (InterruptedException e) {// 1. 无视通知e.printStackTrace();// 2. 直接退出// break;// 3. 进行收尾工作,再退出// System.out.println("此处是后续的处理代码");// break;}}});t.start();try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}t.interrupt();
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

看到第一个程序运行的结果,不知道大家会不会有一个疑惑,使用 isInterrupted()方法是不会清除标志位的,那 3s过后该线程不应该只打印3次 “hello thread !”,接着由于循环的判断条件为 false 而直接退出吗?
其实 isInterrupted() 方法调用后确实不会清除标志位,程序继续运行的原因是因为3s后主线程调用interrupt()方法,解除了 t线程 的阻塞状态,同时 sleep() 引起的异常会顺便清除标志位。

因此,如果将代码改为下面的写法,线程会在当前代码逻辑执行完毕 或 进入下一次循环判断后直接退出。

public static void main(String[] args) {Thread t = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {System.out.println("hello thread !");}System.out.println("---3s后 t线程退出---");});t.start();try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}t.interrupt();
}

程序运行结果如下:
在这里插入图片描述

======

  1. 当前线程处于正常运行状态,将中断标志位置为true。
    从前面我们可以知道:Thread.interrupted() 判断后会清除标志位,isInterrupted() 不会清除标志位,现有以下代码:
private static int count = 100;public static void main(String[] args) {Thread t = new Thread(() -> {for (int i = 1; count > 0 || Thread.currentThread().isInterrupted(); i++) {if(Thread.currentThread().isInterrupted()) {System.out.println("我当前收到中断通知了,这是第 " + i + " 次打印");} else {System.out.println("我当前没有收到中断通知, 这是第 " + i + " 次打印");}count--;if(i == 150) break;}});t.start();try {Thread.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}t.interrupt();
}

程序的运行结果如下:
在这里插入图片描述
这段代码的运行结果是:程序在第58次打印时标志位发生了改变,并且后续的打印不再发生改变,可以预见的是:如果没有使用 i 的值判断使循环退出,程序将会无终止地进行打印。原因就是 isInterrupted()方法 不会清除标志位,因此判断条件永远为真

若把循环中的方法修改为 Thread.interrupted(),程序的运行结果就发生了改变:

private static int count = 50;public static void main(String[] args) {Thread t = new Thread(() -> {for (int i = 0; count > 0 || Thread.interrupted(); i++) {if(Thread.interrupted()) {System.out.println("我当前收到中断通知了,这是第 " + i + " 次打印");} else {System.out.println("我当前没有收到中断通知, 这是第 " + i + " 次打印");}count--;}});t.start();try {Thread.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}t.interrupt();
}

在这里插入图片描述
可以看到在第27次打印时标志位发生了改变,说明此时线程收到了中断通知,但后面的打印内容为“没有收到通知”,且程序最终打印了50次便结束了,这都说明了调用 Thread,interrupted()方法 后标志位被清除了


以上就是本篇文章的全部内容了,如果这篇文章对你有些许帮助,你的点赞、收藏和评论就是对我最大的支持。
另外,文章可能存在许多不足之处,也希望你可以给我一点小小的建议,我会努力检查并改进。

相关文章:

【多线程】认识Thread类及其常用方法

&#x1f4c4;前言&#xff1a; 本文是对以往多线程学习中 Thread类 的介绍&#xff0c;以及对其中的部分细节问题进行总结。 文章目录 一. 线程的 创建和启动&#x1f346;1. 通过继承 Thread 类创建线程&#x1f345;2. 通过实现 Runnable 接口创建线程&#x1f966;3. 其他方…...

多用户商业版 whisper 2.1在线搭建教程

1. 准备工作 购买许可证&#xff1a;确保你已经购买了足够数量的用户许可证&#xff0c;以便所有员工或客户都能使用软件。系统要求&#xff1a;检查你的服务器和客户端计算机是否满足软件的最低系统要求。网络配置&#xff1a;确保你的网络环境&#xff08;如防火墙、路由器等…...

HEXO搭建个人博客

Hexo是一款基于Node.js的静态博客框架&#xff0c;可以生成静态网页托管在GitHub上。中文文档见HEXO 配置环境 安装Git&#xff1a;下载并安装Git 检查git是否正确安装&#xff1a; git --version 安装Node.js&#xff1a;Node.js 为大多数平台提供了官方的安装程序。注意安装…...

Spring MVC学习之——RequestMapping注解

RequestMapping注解 作用 用于建立请求URL和处理请求方法之间的对应关系。 属性 value&#xff1a;指定请求的实际地址&#xff0c;可以是一个字符串或者一个字符串列表。 value可以不写&#xff0c;直接在括号中写&#xff0c;默认就是value值 RequestMapping(value“/hel…...

鸿蒙原生应用/元服务开发-延迟任务开发实现(二)

一、接口说明 接口名接口描述startWork(work: WorkInfo): void;申请延迟任务stopWork(work: WorkInfo, needCancel?: boolean): void;取消延迟任务getWorkStatus(workId: number, callback: AsyncCallback>): void;获取延迟任务状态&#xff08;Callback形式&#xff09;g…...

机器学习在什么场景下最常用-九五小庞

机器学习在多个场景中都有广泛的应用&#xff0c;下面是一些常见的应用场景&#xff1a; 自然语言处理&#xff08;NLP&#xff09;&#xff1a;如语音识别、自动翻译、情感分析、垃圾邮件过滤等。数据挖掘和分析&#xff1a;如市场分析、用户画像、推荐系统、欺诈检测等。智能…...

利用IP应用场景API识别真实用户

引言 在当今数字化时代&#xff0c;随着互联网的普及和应用的广泛&#xff0c;验证用户身份的重要性变得越来越突出。在许多场景中&#xff0c;特别是在涉及安全性、用户体验以及个人隐私保护方面&#xff0c;确定用户的真实身份至关重要。而IP应用场景API则是一种强大的工具&…...

Hugging Face怎么通过国内镜像去进行模型下载(hf-mirror.com)

一、引言 Hugging Face &#x1f917;是一家专注于自然语言处理&#xff08;NLP&#xff09;技术的公司&#xff0c;以其开源贡献和先进的机器学习模型而闻名。该公司最著名的产品是 Transformers 库&#xff0c;这是一个广泛使用的 Python 库&#xff0c;它提供了大量预训练模…...

POKT Network 开启周期性通缩,该计划将持续至 2025 年

POKT Network&#xff08;也被称为 Pocket Network&#xff09;在通证经济模型上完成了重大的改进&#xff0c;不仅将通货膨胀率降至 5% 以下&#xff0c;并使 POKT 通证在 2025 年走向通缩的轨迹上&#xff0c;预计到2024 年年底通货膨胀率将降至 2% 以下。POKT Network 的 “…...

LRU Cache

文章目录 1. 什么是LRU Cache2. LRU Cache的实现3. LRU Cache的OJ题目分析AC代码 1. 什么是LRU Cache LRU是Least Recently Used的缩写&#xff0c;意思是最近最少使用&#xff0c;它是一种Cache替换算法。 什么是Cache&#xff1f; 狭义的Cache指的是位于CPU和主存间的快速RAM…...

软件测试面试题整理

软件测试的几个阶段 在进行Beta测试之前和之后&#xff0c;通常会进行以下几种测试&#xff1a; 内部测试&#xff08;Internal Testing&#xff09; 在Beta测试之前&#xff0c;开发团队会进行内部测试&#xff0c;对软件进行全面的测试。这个阶段包括单元测试、集成测试和系…...

C++三剑客之std::variant(二):深入剖析

目录 1.概述 2.辅助类介绍 2.1.std::negation 2.2.std::conjunction 2.3.std::is_destructible 2.4.std::is_object 2.5.is_default_constructible 2.6.std::is_trivially_destructible 2.7.std::in_place_type和std::in_place_index 3.原理分析 3.1.存储分析 3.2.…...

实验一 安装和使用Oracle数据库

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的…...

软件工程研究生后期总结

写这篇随笔的时候&#xff0c;我已经处于研究生阶段的后期&#xff0c;只剩下一个硕论答辩即可结束研究生生涯。趁有闲暇时间&#xff0c;我希望可以从实习、兼职、论文和求职等几个角度重新整理一下研究生后期的工作和收获&#xff0c;以及对未来工作和生活做出展望。 首先简…...

Java爬虫爬取图片壁纸

Java爬虫 以sougou图片为例&#xff1a;https://pic.sogou.com/ JDK17、SpringBoot3.2.X、hutool5.8.24实现Java爬虫&#xff0c;爬取页面图片 项目介绍 开发工具&#xff1a;IDEA2023.2.5 JDK&#xff1a;Java17 SpringBoot&#xff1a;3.2.x 通过 SpringBoot 快速构建开发环境…...

红队打靶练习:HOLYNIX: V1

目录 信息收集 1、arp 2、netdiscover 3、nmap 4、nikto whatweb 目录探测 1、gobuster 2、dirsearch 3、dirb 4、feroxbuster WEB sqlmap 1、爆库 2、爆表 3、爆列 4、爆字段 后台登录 1、文件上传 2、文件包含 3、越权漏洞 反弹shell 提权 总结 信息…...

elasticsearch[二]-DSL查询语法:全文检索、精准查询(term/range)、地理坐标查询(矩阵、范围)、复合查询(相关性算法)、布尔查询

ES-DSL查询语法&#xff08;全文检索、精准查询、地理坐标查询&#xff09; 1.DSL查询文档 elasticsearch 的查询依然是基于 JSON 风格的 DSL 来实现的。 1.1.DSL 查询分类 Elasticsearch 提供了基于 JSON 的 DSL&#xff08;Domain Specific Language&#xff09;来定义查…...

Microsoft Word 设置底纹

Microsoft Word 设置底纹 References 打开文档页面&#xff0c;选中特定段落或全部文档 在“段落”中单击“边框”下三角按钮 在列表中选择“边框和底纹”选项 在“边框和底纹”对话框中单击“底纹”选项卡 在图案样式和图案颜色列表中设置合适颜色的底纹&#xff0c;单击“确…...

【大数据】Flink 详解(九):SQL 篇 Ⅱ

《Flink 详解》系列&#xff08;已完结&#xff09;&#xff0c;共包含以下 10 10 10 篇文章&#xff1a; 【大数据】Flink 详解&#xff08;一&#xff09;&#xff1a;基础篇【大数据】Flink 详解&#xff08;二&#xff09;&#xff1a;核心篇 Ⅰ【大数据】Flink 详解&…...

workflow源码解析:GoTask

关于go task 提供了另一种更简单的使用计算任务的方法&#xff0c;模仿go语言实现的go task。 使用go task来实计算任务无需定义输入与输出&#xff0c;所有数据通过函数参数传递。 与ThreadTask 区别 ThreadTask 是有模板&#xff0c;IN 和 OUT&#xff0c; ThreadTask 依赖…...

SpringMVC入门案例

引言 Spring MVC是一个基于MVC架构的Web框架&#xff0c;它的主要作用是帮助开发者构建Web应用程序。它提供了一个强大的模型驱动的开发方式&#xff0c;可以帮助开发者实现Web应用程序的各种功能&#xff0c;如请求处理、数据绑定、视图渲染、异常处理等。 开发步骤 1.创建we…...

Docker本地私有仓库搭建配置指导

一、说明 因内网主机需要拉取镜像进行Docker应用&#xff0c;因此需要一台带外主机作为内网私有仓库来提供内外其他docker业务主机使用。参考架构如下&#xff1a; 相关资源&#xff1a;加密、Distribution registry、Create and Configure Docker Registry、Registry部署、D…...

python 通过定时任务执行pytest case

这段Python代码使用了schedule库来安排一个任务&#xff0c;在每天的22:50时运行。这个任务执行一个命令来运行pytest&#xff0c;并生成一个报告。 代码开始时将job_done变量设为False&#xff0c;然后运行预定的任务。一旦任务完成&#xff0c;将job_done设置为True并跳出循…...

算法面试题:合并两个有序链表

描述&#xff1a;给定两个按非递减顺序排列的链表&#xff0c;合并两个链表&#xff0c;并将结果按非递减顺序排列。 例如&#xff1a; # 链表 1: 1 -> 2 -> 4 # 链表 2: 1 -> 3 -> 4合并后的链表应该是&#xff1a;1 -> 1 -> 2 -> 3 -> 4 -> 4 …...

LaWGPT安装和使用教程的复现版本【细节满满】

文章目录 前言一、下载和部署1.1 下载1.2 环境安装1.3 模型推理 总结 前言 LaWGPT 是一系列基于中文法律知识的开源大语言模型。该系列模型在通用中文基座模型&#xff08;如 Chinese-LLaMA、ChatGLM等&#xff09;的基础上扩充法律领域专有词表、大规模中文法律语料预训练&am…...

西门子博途用SCL语言写的入栈出栈

1.用户登录 #pragma code ("useadmin.dll") #include "PWRT_api.h" #pragma code() PWRTLogin(1) 2.用户退出 #pragma code ("useadmin.dll") #include "PWRT_api.h" #pragma code() PWRTLogout(); 3.画面跳转 SetPictureName("P…...

密码产品推介 | 沃通安全电子签章系统(ES-1)

产品介绍 沃通安全电子签章系统&#xff08;ES-1&#xff09;是一款基于密码技术、完全自主研发的商用密码产品&#xff0c;严格遵循国家密码管理局制定的相关标准&#xff0c;可为企业和个人提供安全、合规的电子签章功能服务。产品的主要用途是为各类文书、合同、表单等电子…...

蓝桥杯真题(Python)每日练Day1

说明&#xff1a;在CSP认证的基础上&#xff08;可以看看本人CSP打卡系列的博客&#xff09;备赛2024蓝桥杯&#xff08;Python&#xff09;&#xff0c;本人专业&#xff1a;大数据与数据科学 因此对python要求熟练掌握&#xff0c;通过练习蓝桥杯既能熟悉语法又能锻炼算法和思…...

IDEA怎么用Devtools热部署

IDEA怎么用Devtools热部署 大家知道在项目开发过程中&#xff0c;有时候会改动代码逻辑或者修改数据结构&#xff0c;为了能使改动的代码生效&#xff0c;往往需要重启应用查看改变效果&#xff0c;这样会相当耗费时间。 重启应用其实就是重新编译生成新的Class文件&#xff0…...

boost.circular_buffer的使用和介绍

C 文章目录 C 很多时候&#xff0c;我们需要在内存中记录最近一段时间的数据&#xff0c;如操作记录等。由于这部分数据记录在内存中&#xff0c;因此并不能无限递增&#xff0c;一般有容量限制&#xff0c;超过后就将最开始的数据移除掉。在stl中并没有这样的数据结构&#xf…...