【后端开发】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)😊 😊 😊

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


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

                                                                 😊😊  期待你的关注~~~

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=a8gd7o9p8adx

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

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

相关文章

Android OpenGLES2.0开发(三):绘制一个三角形

我们总是对陌生人太客气&#xff0c;而对亲密的人太苛刻 上一篇文章中&#xff0c;我们已经将OpenGL ES环境搭建完成。接下来我们就可以开始我们的绘图之旅了。该篇我们讲解最基本图形三角形的绘制&#xff0c;这是一切绘制的基础。在OpenGL ES的世界里一切图形都可以由三角形拼…

OrangePi 烧录镜像步骤

理解&#xff1a;第一步&#xff1a;烧录镜像。第二步&#xff1a;建立编译环境&#xff08;一般是PC端的Linux虚拟机&#xff09;和板卡端的文件连接。因为要传文件&#xff0c;一般用挂载的方法。第三步&#xff1a;软件程序的编译与部署。 第一步&#xff1a;烧录镜像步骤 …

数据分析:Python语言网络图绘制

文章目录 介绍加载R包类别导入数据下载数据画图介绍 网络图是一种图形表示法,用于展示实体之间的关系。在不同的领域中,网络图有着不同的含义和用途:在生物学中,网络图可以用来表示生物分子之间的相互作用,如蛋白质相互作用网络。 加载R包 import pandas as pd import …

Xcode 16 上传AppStore遇到第三方库 bitcode 的问题

Xcode 16 上传AppStore遇到第三方库 bitcode 的问题 最近两天更新了Xcode 16&#xff0c;然后正好要发布新版本的App&#xff0c;打包Adhoc没问题&#xff0c;但是上传AppStoreConnect或者TestFlight就不行解决方案参考资料 最近两天更新了Xcode 16&#xff0c;然后正好要发布新…

HTML+CSS学习笔记

目录 HTML 1.开发环境 2.创建HTML文件 3.HTML元素 3.1HTML文件结构 3.2HTML标签 3.3HTML属性​编辑​编辑 3.4HTML区块 3.4.1块元素 3.4.2行内元素 3.5HTML表单 CSS 1.CSS简介 2.CSS语法​编辑 3.CSS三种导入方式 内联样式 内部样式 外部样式 4.选择器​ 5.C…

sheng的学习笔记-AI-时序差分学习

AI目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 强化学习&#xff1a;sheng的学习笔记-AI-强化学习&#xff08;Reinforcement Learning, RL&#xff09;-CSDN博客 蒙特卡罗强化学习&#xff1a; sheng的学习笔记-AI-蒙特卡罗强化学习-CSDN博客 什么是时序差分学习 时序…

解锁HTML的力量:从基础标签到完整网页构建

在整个学习编程技能的过程中&#xff0c;我们会始终基于编程的本质&#xff1a;输入-》函数处理-》输出 和编程语言的本质&#xff1a;语法糖、变量、基础函数&#xff0c;去理解各种编程技术和学习相关的技能。 今天开始学习编程的第一个技能点&#xff1a;HTML。正如编程的本…

国内可用ChatGPT-4中文镜像网站整理汇总【持续更新】

一、GPT中文镜像网站 ① yixiaai.com 支持GPT4、4o以及o1&#xff0c;支持MJ绘画 ② chat.lify.vip 支持通用全模型&#xff0c;支持文件读取、插件、绘画、AIPPT ③ AI Chat 支持GPT3.5/4&#xff0c;4o以及MJ绘画 二、模型知识 o1/o1-mini&#xff1a;最新的版本模型&am…

RabbitMQ 快速入门

目录 什么是MQ 为什么要使用 MQ MQ 的分类 MQ 的选择 认识 RabbitMQ RabbitMQ 的核心部分 安装 脚本安装 docker 安装 启动 web 管理界面 创建用户 创建消息队列 基本概念 消息应答 持久化 预取值 发布确认 交换机 Exchange 概念 死信队列 死信的来源 延迟…

深度学习03-神经网络01-什么是神经网络?

神经网络的基本概念 人工神经网络&#xff08;Artificial Neural Network&#xff0c;ANN&#xff09;&#xff1a; 是一种模仿生物神经网络的计算模型。由多个神经元&#xff08;或称为节点&#xff09;组成&#xff0c;这些节点通过不同的连接来传递信息。 每个神经元可以接…

淘客系统开发之卷轴模式系统源码功能分析

随着互联网技术的快速发展&#xff0c;电商行业不断创新&#xff0c;探索更加高效、有趣的用户参与机制。其中&#xff0c;卷轴模式作为一种新兴的商业模式&#xff0c;以其独特的积分兑换和任务系统&#xff0c;在淘客系统开发中得到了广泛应用。本文将从技术角度&#xff0c;…

Kafka-Manager安装及操作

文章目录 一、kafka-manager介绍二、kafka-manager安装三、Kafka-Manager操作 一、kafka-manager介绍 CMAK (Cluster Manager for Apache Kafka, previously known as Kafka Manager) CMAK (previously known as Kafka Manager) is a tool for managing Apache Kafka cluster…

LeetCode 每周算法 6(图论、回溯)

LeetCode 每周算法 6&#xff08;图论、回溯&#xff09; 图论算法&#xff1a; class Solution: def dfs(self, grid: List[List[str]], r: int, c: int) -> None: """ 深度优先搜索函数&#xff0c;用于遍历并标记与当前位置(r, c)相连的所有陆地&…

切换淘宝最新npm镜像源

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;前端工程师 文章目录 一、&#x1f30e;前言二、&#x1f30e;切换淘宝最新npm镜像源2.…

[Linux] Linux操作系统 进程的状态

标题&#xff1a;[Linux] Linux操作系统 进程的状态 个人主页&#xff1a;水墨不写bug &#xff08;图片来源于网络&#xff09; 目录 一、前置概念的理解 1.并行和并发 2.时间片 3.进程间具有独立性 4.等待的本质 正文开始&#xff1a; 在校的时候&#xff0c;你一定学过《…

9.25度小满一面

1.map的底层 2.unorder_map哈希表有自己实现过吗&#xff1f;哈希冲突 3.poll和epoll和select的优缺点、 4.线程同步机制是用来做什么的? 5.五子棋项目问题-- 算法题: 6.LeetCode.重排链表 给定一个单链表 L 的头节点 head &#xff0c;单链表 L 表示为&#xff1a; L0…

通信工程学习:什么是VPN虚拟专用网络

VPN:虚拟专用网络 VPN(Virtual Private Network),即虚拟专用网络,是一种通过公共网络(如互联网)建立私有网络连接的技术。以下是关于VPN的详细解释: 一、VPN虚拟专用网络的定义与原理 VPN通过公共网络(通常是互联网)建立一个临时的、安全的连接,形…

JavaEE: 深入探索TCP网络编程的奇妙世界(四)

文章目录 TCP核心机制TCP核心机制四: 滑动窗口为啥要使用滑动窗口?滑动窗口介绍滑动窗口出现丢包咋办? TCP核心机制五: 流量控制 TCP核心机制 上一篇文章 JavaEE: 深入探索TCP网络编程的奇妙世界(三) 书接上文~ TCP核心机制四: 滑动窗口 为啥要使用滑动窗口? 之前我们讨…

stm32单片机个人学习笔记6(EXTI外部中断)

前言 本篇文章属于stm32单片机&#xff08;以下简称单片机&#xff09;的学习笔记&#xff0c;来源于B站教学视频。下面是这位up主的视频链接。本文为个人学习笔记&#xff0c;只能做参考&#xff0c;细节方面建议观看视频&#xff0c;肯定受益匪浅。 STM32入门教程-2023版 细…

thinkphp8 从入门到放弃(后面会完善用到哪里写到哪)

thinkphp8 从入门到放弃 引言 thinkphp* 大道至简一、 thinkphp8 安装安装Composerthinkphp 安装命令(tp-项目名称)多应用安装&#xff08;一个项目不会只有一个应用&#xff09;安装完文件目录如下本地部署配置伪静态好了项目可以run 二、架构服务&#xff08;Service&#xf…