【后端开发】JavaEE初阶—Theard类及常见方法—线程的操作(超详解)

前言:

🌟🌟本期讲解多线程的知识哟~~~,希望能帮到屏幕前的你。

🌈上期博客在这里:【后端开发】JavaEE初阶—线程的理解和编程实现-CSDN博客

🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客

目录

📚️1.引言

📚️2.Thread常见的构造方法

2.1创建对象名字

1.第一种创建线程对象,并命名

2.使用Runnable对象,并命名

3.检查线程名字

 2.2线程组

 📚️3.Theard类常见属性

3.1属性ID与名称

3.2是否后台线程

 3.3是否存活

 📚️4.启动线程

4.1多次启动线程

 4.2实现多次启动线程

4.3start()与run()使用

📚️5.终止一个线程

5.1设置标志位isQuit

5.2标志位的替代

 📚️6.等待线程

📚️7.获取线程引用

7.1使用Thread继承

7.2不使用Thread继承 

 📚️8.总结

 

📚️1.引言

 Hello!!! 小伙伴们咱们又见面啦,继小编上期讲解了多线程编程的相关重要知识,以及如何实现线程的创建之后,本期将继续讲解关于多线程的相关方法和属性,开始发车了加油加油~~~🥳🥳🥳

且听小编讲解,包你学会!!!

📚️2.Thread常见的构造方法

主要方法如下:

这里的创建线程对象,以及使用Runnable对象创建线程小编是在上期是讲解过的,想要了解的小伙伴可以去小编主页看看;那么接下来就来几个陌生的方法吧

2.1创建对象名字

1.第一种创建线程对象,并命名

代码如下:

Thread t=new Thread(()->{System.out.println("这是thread线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}},"我的线程");

 注意:小编这里使用了lambda表达式进行演示,其中在方法体内进行了补充,在方法体后,用双引号表示的就是为这个线程所编写的名字;

2.使用Runnable对象,并命名

代码如下:

Thread t1=new Thread(new Runnable() {@Overridepublic void run() {}},"这是我的线程");

注意:此时小编没有对run方法进行重写,但是仍然对线程进行了命名;

3.检查线程名字

这里就要用到JDK中的jconsole了,这个在JDK中存在;

然后点击进入jconsole,并进行我们创建的线程的连接,图片展示:

很明显这里小编创建的项目是threadDemo6,并点击连接,找到线程,就可以发现我们创建线程的名字了,图片展示如下:

这就是我们创建线程的名字啦~~~

注意:在执行上述操作时,一定要保证从开始到结束中,idea上的线程代码要跑起来,否则在连接处就无法找到我们创建的线程; 

当然这里也可以使用getName()方法,小编会在后面演示~~~

 2.2线程组

这里的线程组是java中的概念,和系统内核中的线程组是不一样的;

 线程组的作用:

1. 组织线程:可以将多个线程归为一个线程组,方便对相关线程进行整体操作和管理。
2. 控制权限:线程组可以控制其包含的线程的访问权限,例如设置是否允许某个线程组中的线程修改系统资源等。
3. 异常处理:当一个线程组中的某个线程抛出未捕获的异常时,线程组可以对这个异常进行统一的处理。

这里的线程组,咱们了解即可~~~

 📚️3.Theard类常见属性

常见属性如下:

这里的getState()方法就是描述线程的状态,进程存在就绪状态与阻塞状态,那么线程也存在对应的状态,以及这里的getPriority()方法描述的是线程的调度优先级,但是效果不明显,至于收否被中断,后面小编会进行讲解;接下来即小编重点实例讲解的了;

3.1属性ID与名称

如何获取线程的ID与名称;代码如下:

Thread t=new Thread(()->{System.out.println("这是thread线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}},"我的线程");
t.start();System.out.println(t.getId()+"  "+t.getName());

注意:在通过Theard实例调用线程的ID和名字时,对应的是JVM自动分配的身份标识,对于名字一般是Thread-0,Thread-1......但是自主给线程起名后,对应的名字也会变为所起名字;

3.2是否后台线程

按照之前的理解,当main函数执行结束后,整个函数就应该执行结束了,但是在多线程中并不是如此,因为JVM内置线程为后台线程,而后台线程不会阻止线程结束

我们创建的代码线程默认为前台线程,前台线程会阻止线程结束,所以即使main函数执行完了,那么线程仍然不会结束此时;

此时我们就要将前台线程改为后台线程,代码如下:

 public static void main(String[] args) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {while (true) {System.out.println("hello thread");}}}, "这是我的线程");// 在 start 之前, 设置线程为 后台线程 (不能在 start 之后设置)t.setDaemon(true);t.start();}

此时主线程走完了就不会有任何输出:

注意:前台线程会阻止进程结束,而后台线程不会阻止进程结束,并且在改写前台线程为后台线程时,必须在创建线程之前!!!

 3.3是否存活

这里的是否存活表示的是内核中的PCB线程是否存在;

Thread实例,虽然来说表示的是一个线程,但是这里和内核中的PCB线程的生命周期是不一样的

代码实例如下:

 Thread t=new Thread(()->{System.out.println("这是thread线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}},"我的线程");System.out.println("线程创建之前"+t.isAlive());//falset.start();System.out.println("线程启动之后"+t.isAlive());//trueThread.sleep(3000);//保证t线程结束System.out.println("t线程结束之后"+t.isAlive());//false

这里的输出结果就如同上述注解;

注意:内核中的PCB线程是在t调用start方法后创建线程才会存在,当线程中run()方法执行完后,内核PCB被释放了,此时isAlive表示为没有存活;

这里的主线程休眠是为了保证另一线程执行完毕;

 📚️4.启动线程

对于启动线程来说就使用start方法就行了,但是这里要进行扩展

4.1多次启动线程

代码如下:

Thread t=new Thread(()->{System.out.println("这是thread线程");},"我的线程");t.start();t.start();

这里小编进行了线程的两次启动,那么结果就是:

很明显这里抛出异常了即:非法的线程状态异常~~~

注意:线程只能启动一次,一个线程实例对象,多次调用start方法就会抛出非法线程状态异常

 4.2实现多次启动线程

由于上述讲到,一个线程实例化对象不能多次启动线程,那么对于多次启动线程,就要实现多个线程,多个启动

代码实例如下:

public class ThreadDemo10 {public static void main(String[] args) {Thread t1 = new Thread(() -> {System.out.println("hello1");});Thread t2 = new Thread(() -> {System.out.println("hello2");});t1.start();t2.start();}

此时我们创建了两个线程,这里就可以实现多次启动线程啦~~~

4.3start()与run()使用

对于这两种方法,两者其实是互不相干的;

start()方法:是通过系统调用API实现了一个线程的创建,其中的JVM在创建好线程后自动调用run方法,此时就有多个线程;

run()方法:就是之前在JavaSE部分中,类中方法的调用一致,但是这里调用后,没有创建新的现场,仍然为单线程;

代码实例:

class MyThread4 extends Thread {@Overridepublic void run() {while (true) {System.out.println("hello thread");}}
}public class ThreadDemo11 {public static void main(String[] args) {Thread t = new MyThread4();t.start();// t.run();while (true) {System.out.println("hello main");}}

注意:在如上述代码中,当调用start方法后,会创建两个线程,那么会不断输出(hello thread)和(hello main)这两个输出交替出现,但是调用run方法,就会陷入死循环打印(hello thread)不会向下执行主线程了;

📚️5.终止一个线程

这里终止一个线程就是:让线程run方法执行完毕;

5.1设置标志位isQuit

代码如下:

 public static boolean isQuit=true;public static void main(String[] args) throws InterruptedException {Thread t=new Thread(()->{while (isQuit){System.out.println("这是一个thread线程,在工作");try {Thread.sleep(2000);}catch (InterruptedException e){throw new RuntimeException();}}System.out.println("线程结束");});t.start();Thread.sleep(4000);System.out.println("让t线程结束");isQuit=false;

此时的输出结果就是:

此时我们想让线程结束,那么就要将循环内的判断条件进行改变,即将isQuit的值变为false,并且为了观察,进行必要的输出打印;

 那么我们将代码中的isQuit改为主线程的局部变量,就是不可行的!!!

因为lambda表达式/匿名内部类存在变量捕获的问题,此时这里的isQuit已经被final修饰了,并且匿名内部类是可以访问外部类的成员变量的,所以不可以改为main函数的局部变量;

5.2标志位的替代

这里就用Thread.currentThread().isInterrupted()来代替isQuit,但是这里表示为false,interrupt表示修改值

代码如下:

 public static void main(String[] args) throws InterruptedException {Thread t=new Thread(()->{while (!Thread.currentThread().isInterrupted()){System.out.println("线程在工作");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("工作结束");});t.start();Thread.sleep(4000);System.out.println("结束线程工作");t.interrupt();//类似改变标志的布尔值}

此时我进行结果打印时,发现抛出异常了:

当我们输出结束线程工作时,这里没有结束继续工作,并抛出异常:休眠终止异常~~~

注意:

在执行 sleep时调用interrupt会导致sleep提前唤醒;会导致抛出上述异常,或者将Thread实例中的isInterrupted()标志位,改为false,这就会导致程序继续执行;

解决方法 :

此时我们就可以加上break,让线程立即结束,代码如下:

 while (!Thread.currentThread().isInterrupted()){System.out.println("线程在工作");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();// break;}}

当然或者直接省去sleep休眠状态,直接一直工作知道终止线程;

经过以上解释,程序的终止是一种软性操作,需要线程的配合才能实现!!!

 📚️6.等待线程

由于线程是调度执行的,底层调度是不确定的,但是可以通过一些API来影响线程的执行顺序,此时join就提供了这样的操作;

代码如下:

 public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {int n = 5;for (int i = 0; i < n; i++) {System.out.println("我是一个线程, 正在工作中...");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("线程执行结束");});t.start();// 这个操作就是线程等待.t.join();System.out.println("这是主线程, 期望这个日志在 t 结束后打印");}

 那么此时小编就使用了join来等待线程的执行完毕,再输出主线程,输出结果:

那么此时达到了我们的要求;但是我们运用sleep方法也可以实现,为啥不用它呢???

解答:在使用sleep方法时要知道线程结束的具体时间,因为休眠时间是要根据线程执行完毕才能够实现的,而使用join就不必知道这一点;

注意:

在使用join方法时要进行异常的抛出;

在那个线程使用join方法,谁就处于等待状态,而调用的线程实例就是被等待的一方;

使用sleep方法可以实现上述需求,但是需要知道线程的执行时间,所以不推荐;

join中也可以添加等待时间,如果不添加就是“死等”状态,一般在计算机中不推荐死等

 这里也可以使用线程等待来实现一系列运算;

public static long sum=0;public static void main(String[] args)throws InterruptedException {Thread t1=new Thread(()->{for (long i = 0; i <50 ; i++) {sum+=i;}});Thread t2=new Thread(()->{for (long i = 50; i <=100 ; i++) {sum+=i;}});t1.start();t2.start();long begin=System.currentTimeMillis();t1.join();t2.join();long end=System.currentTimeMillis();System.out.println("sum="+sum);System.out.println("等待时间"+(end-begin)+"ms");}

这里小编就使用多线程,和等待机制来实现算数相加,此时在两个线程开启后,在让主线程进行等待,这里使用时间打印可以来表示单个线程和多个线程来执行这个任务的时间相差,小编这里就不再过多演示;

📚️7.获取线程引用

7.1使用Thread继承

代码如下:

 public static void main(String[] args) throws InterruptedException {Thread t=new mythread7();t.start();   }
}
class mythread7 extends Thread{@Overridepublic void run(){System.out.println(this.getId()+" "+this.getName());}
}

注意:在Thread继承中可以使用this拿到线程实例;

7.2不使用Thread继承 

代码如下:

 public static void main(String[] args) throws InterruptedException {Thread t=new Thread(()->{Thread t1=Thread.currentThread();System.out.println(t1.getId());});t.start();}

注意:如果是Runnable或者是lambda表达式,this就无法指向Thread对象,这个时候就要用到Thread.currentThread()

至于为啥不直接使用(t)来得到 属性:

在 Lambda 表达式中,变量必须是最终变量或有效最终变量,而 t 是一个非最终变量,因为他还没有完全初始化

 📚️8.总结

💬💬小编本期讲解了关于线程的某些重要属性和方法,例如线程的启动,终止,等待以及常见的属性的获取方法和构造方法,并附上代码供小伙伴们参考~~~

代码在这里哟:GGBondlctrl/Thread (gitee.com)😊 😊 😊

🌅🌅🌅~~~~最后希望与诸君共勉,共同进步!!!


💪💪💪以上就是本期内容了, 感兴趣的话,就关注小编吧。

                                                                 😊😊  期待你的关注~~~

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

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

相关文章

WIFI路由器的套杆天线简谈

❝本次推文简单介绍下WIFI路由器的套杆天线。 路由器天线 路由器在这个万物互联的时代&#xff0c;想必大家对其都不陌生。随着科技的发展&#xff0c;常用的路由器上的天线也越来越多&#xff0c;那么问题来了&#xff1a;天线越多&#xff0c;信号越好吗&#xff1f;路由器…

ChromaDB教程_2024最新版(上)

前言 在上一篇&#xff08;快捷入口&#xff09;文章中&#xff0c;博主提到了一个向量存储&#xff0c;其中用到了Chroma数据库。代码示例如下&#xff1a; vectordb Chroma.from_documents(documentsdocs,embeddingembedding,persist_directoryvector_dir )这是基于langc…

服务发现和代理实例的自动更新

☞ 返回总目录 1.服务发现的两种方式 StartFindService 方法 这是一个在后台启动的连续 “FindService” 活动&#xff0c;当服务实例的可用性发生变化时&#xff0c;会通过回调通知调用者。 它返回一个FindServiceHandle&#xff0c;可通过调用StopFindService来停止正在进行…

自动化立体仓库与堆垛机单元的技术参数

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。 新书《智能物流系统构成与技术实践》人俱乐部 完整版文件和更多学习资料&#xff0c;请球友到知识星球【智能仓储物流技术研习社】自行下载。 这份文件是一份自动化立体仓库与堆垛机单…

初识APC机制实现APC注入

参考&#xff1a;APC异步过程调用-CSDN博客 又是跟着红队蓝军师傅学免杀的一天&#xff0c;这节课介绍了APC机制和APC注入的实现。 APC介绍&#xff1a; APC&#xff0c;全称为Asynchronous Procedure Call&#xff0c;即异步过程调用&#xff0c;是指函数在特定线程中被异…

【HTTPS】对称加密和非对称加密

HTTPS 是什么 HTTPS 是在 HTTP 的基础上&#xff0c;引入了一个加密层&#xff08;SSL&#xff09;。HTTP 是明文传输的&#xff08;不安全&#xff09; 当下所见到的大部分网站都是 HTTPS 的&#xff0c;这都是拜“运营商劫持”所赐 运营商劫持 下载⼀个“天天动听“&…

Java ETL - Apache Beam 简介

基本介绍 Apache Beam是一个用于大数据处理的开源统一编程模型。它允许用户编写一次代码&#xff0c;然后在多个批处理和流处理引擎上运行&#xff0c;如Apache Flink、Apache Spark和Google Cloud Dataflow等。Apache Beam提供了一种简单且高效的方式来实现数据处理管道&…

【Node.js】初识微服务

概述 Node.js 的微服务架构是一种通过将应用程序分解为独立的、松耦合的小服务的方式进行系统设计。 每个微服务负责处理一个特定的业务功能&#xff0c;并且这些服务可以独立开发、部署、扩展和管理&#xff0c;并且可以通讯。 它的核心思想就是解耦。 微服务和微前端是类…

智慧校园建设解决方案建设系统简介

一、建设背景 1.1 政策背景 1.2 班牌的演变 1.3 建设愿景 二、 智慧班牌简介 三、智慧班牌系统 3.1 系统概述 3.2 软件平台功能交互简介 3.2.1 智慧班牌与管理平台间的功能关联 3.2.2 手机客户端&#xff08;管理员、教师、家长端&#xff09; 3.2.3 手机客户端&#x…

CleanClip --- 为Mac用户打造的智能剪贴板管理利器

CleanClip是一款专为Mac用户设计的强大剪贴板管理工具&#xff0c;旨在提升用户的工作效率和数据管理体验。它通过智能化的剪贴板内容管理&#xff0c;实现了Mac系统与用户操作之间的无缝衔接。CleanClip支持多种连接方式&#xff0c;包括系统级的快捷操作和自定义快捷键&#…

PHP API 框架:构建高效API的利器

在当今快速发展的互联网时代&#xff0c;API&#xff08;应用程序编程接口&#xff09;已成为连接不同应用程序和服务的关键。PHP&#xff0c;作为一种流行的服务器端脚本语言&#xff0c;提供了多种强大的框架来简化API的开发。本文将介绍PHP API框架的重要性&#xff0c;以及…

【宠物小精灵之收服(待更新)】

题目 代码 #include <bits/stdc.h> using namespace std; int f[1010][510]; int main() {int n, m, k;cin >> n >> m >> k;int c 0;for(int i 1; i < k; i){int cost, hp;cin >> cost >> hp;for(int j n; j > cost; j--){for(i…

yarn : 无法加载文件 C:\Users\Rog\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁止运行脚本

yarn : 无法加载文件 C:\Users\Rog\AppData\Roaming\npm\yarn.ps1&#xff0c;因为在此系统上禁止运行脚本 设置命令行窗口默认以管理员身份运行&#xff0c;在此基础上输入以下代码&#xff0c;应该就好使了&#xff0c;切记&#xff0c;以下代码才是关键&#xff0c;我基本上…

肾癌的多模态预测模型-临床-组织学-基因组

目录 摘要 技术路线 ① lncRNA的预测模型 ②病理 WSI 的分类器 ③临床病理分类器 模型结果 与别的模型比较 同行评审学习 1&#xff09;使用lncRNA的原因 2&#xff09;模型临床使用意义 3&#xff09;关于截止值的使用 摘要 A multi-classifier system integrated…

树莓派3B驱动ST7735(内核)(代码篇)(TODO)

书接上回&#xff1a;树莓派3B驱动ST7735&#xff08;内核&#xff09;&#xff08;配置篇&#xff09;_st7735s驱动固件下载-CSDN博客&#xff0c;这次主要是精读一下树莓派内核中的ST7735驱动源码 &#xff08;TODO&#xff09;

Elasticsearch不停机切换(上云)方案

如何给飞行中的飞机换引擎? 背景 业务背景 略 技术背景 线下集群40个索引左右&#xff0c;总数据量不大,不到100G因为ES承担的业务鉴权业务&#xff0c;所以不能接受停机割接 还有就是ES中数据来自各个业务方&#xff0c;推送的时机不定&#xff0c;也没有完备的重推机制&…

ChatGPT 在国内使用的方法

AI如今很强大&#xff0c;聊聊天、写论文、搞翻译、写代码、写文案、审合同等等&#xff0c;ChatGPT 真是无所不能~ 作为一款出色的大语言模型&#xff0c;ChatGPT 实现了人类般的对话交流&#xff0c;最主要是能根据上下文进行互动。 接下来&#xff0c;我将介绍 ChatGPT 在国…

重生之我们在ES顶端相遇第15 章 - ES 的心脏-倒排索引

文章目录 前言为什么叫倒排索引数据结构如何生成如何查询TF、IDF参考文档 前言 上一章&#xff0c;简单介绍了 ES 的节点类型。 本章&#xff0c;我们要介绍 ES 中非常重要的一个概念&#xff1a;倒排索引。 ES 的全文索引就是基于倒排索引实现的。 本章内容建议重点学习&…

Win10 安装Node.js 以及 Vue项目的创建

一、Node.js和Vue介绍 1. Node.js Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。它允许你在服务器端运行 JavaScript&#xff0c;使得你能够使用 JavaScript 来编写后端代码。以下是 Node.js 的一些关键特点&#xff1a; 事件驱动和非阻塞 I/O&#xff1a;Node…

Parallels Desktop 20(Mac虚拟机) v20.0.0 for Mac 最新破解版(支持M系列)

Parallels Desktop 20 for Mac 正式发布&#xff0c;完全支持 macOS Sequoia 和 Windows 11 24H2&#xff0c;并且在企业版中引入了全新的管理门户。 据介绍&#xff0c;新版本针对 Windows、macOS 和 Linux 虚拟机进行了大量更新&#xff0c;最大的亮点是全新推出的 Parallels…