浅谈Thread类及常见方法与线程的状态(多线程编程篇2)

目录

 前言 

1.Thread类及常见方法

Thread类中常见的属性

1. getId()

2. getName()

3. getState()

4. getPriority()

5. isDaemon()

6. isAlive()

7. isInterrupted()

2.Thread类中常见的方法

Thread.interrupt() (中断线程)

Thread.start()(启动线程)

1. 覆写 run() 方法

2.调用start() 

Thread.join()(等待线程)

3.线程的状态

1. NEW(新建)

2. RUNNABLE(运行中)

3. BLOCKED(阻塞)

4. WAITING(无限等待)

5. TIMED_WAITING(限时等待)

6. TERMINATED(终止)

结尾

 前言 

如何简单的去使用jconsloe 查看线程 (多线程编程篇1)_如何查看weblogic当前线程-CSDN博客

距离笔者发布本系列第一篇博客已过去一月有余,本篇博客,笔者将继续介绍已经整理学习好的内容

既帮助未来的自己复习,也为正在阅读的你提供参考和思考.

本篇博客的内容正如标题所示,笔者主要将介绍一下内容

1.Thread类,并通过举例演示常见方法

2.向大家介绍线程的状态.

愿我们一起进步!

1.Thread类及常见方法

首先,需要说明的是,相比于多进程编程,多线程编程能够更有效地利用多核CPU资源。

线程作为进程中的执行单元,它们共享进程的内存空间,因此在创建、调度和销毁时所消耗的资源远少于进程。由于线程之间的开销较小,因此在多个线程之间的调度和切换效率较高。

操作系统内核实现了线程这样的机制 , 并且对用户层提供了一些 API 供用户使用 .
Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进行了进一步的抽象和封装 .降低我们的学习成本
关于如何创建线程,笔者在上文已经介绍过了,笔者常用Lambda表达式去创建实例化一个Thread类

Thread类中常见的属性

如何简单的去使用jconsloe 查看线程 (多线程编程篇1)_如何查看weblogic当前线程-CSDN博客

1. getId()

  • 功能:获取线程的唯一标识符(ID),和PID类似。每个线程都有一个唯一的 ID,它是一个正整数。这个ID是JAVA给分配的,不是系统API提供的,更不是PCB中的ID.

  • 用途:可以用来标识不同的线程。

2. getName()

  • 功能:获取线程的名称。每个线程都有一个名字,默认是 Thread-0Thread-1 等,当然也可以通过构造方法手动设置线程名称。

  • 用途:帮助开发者区分不同的线程

3. getState()

  • 功能:获取线程的状态,返回一个 Thread.State 枚举值。

  • 用途:用于查看线程的当前状态

4. getPriority()

  • 功能:获取线程的优先级。

  • 用途:用于控制线程的调度顺序,优先级高的线程会比优先级低的线程更早得到 CPU 时间。

5. isDaemon()

  • 功能:检查线程是否为守护线程(也可以叫做后台线程)(Daemon thread)。守护线程是辅助性线程,在所有非守护线程结束后会自动退出。

  • 用途:判断线程是否为后台线程。守护线程一般用于系统服务或后台任务,如垃圾回收线程。

一般情况下,我们会默认一个线程为前台线程,一个JAVA进程中,如果前台线程没有执行结束,那么整个进程是一定不会结束的,但是后台进程是否结束了,不会影响整个进程的进行

 举一个简单的例子 :

public class Demo2
{public static void main(String[] args) {Thread y = new Thread(() -> {while (true) {System.out.println("Test");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();throw new RuntimeException(e);}}}, "线程一"); // lambda 表达式写法y.setDaemon(true); // 设置为后台线程y.start(); // 启动线程}
}

如果在这里设置成后台线程,那么运行结果如下 


进程已结束,退出代码0

或者 

Test
进程已结束,退出代码0

如果y是一个前台线程,那么该程序应该一直打印"Test",但是我们在这里把他设置为了后台线程, 

此时主线程立马就执行完了,由于没有其他前台线程了,因此进程结束了,y线程来不及执行,或者只能执行几次.

6. isAlive()

  • 功能:检查线程是否处于活动状态。如果线程已经启动并且还没有终止,则返回 true,否则返回 false

  • 用途:用于判断一个线程是否仍然在运行。

举个例子

public class Demo2
{public static void main(String[] args) throws InterruptedException {Thread y = new Thread(() -> {System.out.println("Test");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();throw new RuntimeException(e);}}, "线程一"); // lambda 表达式写法y.start(); // 启动线程System.out.println(y.isAlive());Thread.sleep(2000);System.out.println(y.isAlive());}
}

结果如下:

true
Test
false进程已结束,退出代码0

 启动y线程后,线程存活,等待两秒后,线程的活动被终止

7. isInterrupted()

  • 功能:检查线程是否被中断。如果线程被中断,返回 true,否则返回 false

  • 用途:判断线程是否接收到中断信号。

2.Thread类中常见的方法

Thread.interrupt() (中断线程)

  • 作用:中断线程的执行。

  • 用途:通过调用该方法来中断一个线程的执行,线程可以通过 interrupted()isInterrupted() 方法判断是否被中断。

举个例子

结合我们刚刚提到的isInterrupted(),我们举一个例子

下面是一个线程执行的示例,线程会不断打印 "正在工作",直到被 interrupt() 发送中断信号后,它会检查自身的 isInterrupted() 状态并退出。

public class Demo4 {public static void main(String[] args) {Thread thread = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) { System.out.println("正在工作");try {Thread.sleep(1000); // 线程进入休眠状态} catch (InterruptedException e) {System.out.println("线程被中断,抛出异常终止");//  如果选择抛出异常,线程会立即终止// 当捕获到 InterruptedException 时,异常被重新抛出,// 这会导致当前线程的执行停止,不会继续执行 while 循环后面的代码。// 也就是说,抛出 RuntimeException 可能会导致线程提前结束。// throw new RuntimeException(e);// ✅ 如果使用 break,则线程可以优雅退出// break;}}// ⚠ 只有在没有抛出异常时,这行代码才会执行System.out.println("工作结束"); });thread.start();try {Thread.sleep(3000); // 主线程等待 3 秒} catch (InterruptedException e) {e.printStackTrace();}thread.interrupt(); // 发送中断信号System.out.println("已通知线程中断");}
}

值得注意的就是这里要不要  throw new RuntimeException(e)

而关于Thread.currentThread()

在 Java 中,Thread.currentThread() 返回当前正在执行的线程对象。因为 thread 变量指向的是主线程创建的线程实例,而 Thread.currentThread() 总是指向当前正在运行的线程,因此在线程内部调用 Thread.currentThread().isInterrupted() 可以正确获取线程的中断状态。

并且,即使线程内部的逻辑出现阻塞,也可以用这个方法唤醒,正常来说,sleep会休眠到时间到了才唤醒,但是interrupt()方法 可以使sleep内部出发异常,从而被提前唤醒,如果我们自己定义标注为,就做不到

效果如下:

 但是sleep方法抛出异常,同时也会自动清除刚才设置的标志位,所以我们的线程会继续工作,所以我们需要break优雅地去中断.

我们再总结一下

1.如果只捕获异常,不做处理

try {Thread.sleep(1000);
} catch (InterruptedException e) {e.printStackTrace(); // 仅打印异常
}

标志位会被清除,但是线程不会停止,将继续工作

2.使用break;

try {Thread.sleep(1000);
} catch (InterruptedException e) {e.printStackTrace(); // 仅打印异常break;
}
  • 线程不会抛出异常,而是优雅退出循环

  • while 结束后,线程会执行 "工作结束" 语句并结束。

 3.你选择抛出异常

try {Thread.sleep(1000);
} catch (InterruptedException e) {throw new RuntimeException(e);
}
  • 线程会立即终止,不会执行 while 循环后面的代码

  • "工作结束" 也不会被打印,因为 while 之后的代码永远不会执行。

读者们可以把代码拷到自己的环境里去试验一下,笔者下的结论建立在试验过的基础之上.

Thread.start()(启动线程)

这里笔者主要是谈一下覆写RUN方法和调用start()方法的区别,它们有什么区别呢?

1. 覆写 run() 方法

run() 方法是 Thread 类的一个普通方法,如果我们直接调用 run(),它不会启动一个新的线程,而是作为当前线程中的一个普通方法执行。例如:

class MyThread extends Thread {@Overridepublic void run() {System.out.println("线程正在执行: " + Thread.currentThread().getName());}
}public class Main {public static void main(String[] args) {MyThread t1 = new MyThread();t1.run(); // 直接调用 run()System.out.println("主线程执行完毕");}
}

效果如下:

 

 可以看到,run() 方法是在 主线程 中执行的,而不是在一个新的线程中。

2.调用start() 

start() 方法是 Thread 类的核心方法,调用 start() 后,JVM 会创建一个新的线程,并调用该线程的 run() 方法,从而实现真正的并发执行。例如:

class MyThread extends Thread {@Overridepublic void run() {System.out.println("线程正在执行: " + Thread.currentThread().getName());}
}public class Main {public static void main(String[] args) {MyThread t1 = new MyThread();t1.start(); // 启动新线程System.out.println("主线程执行完毕");}
}

代码效果:

主线程执行完毕
线程正在执行: Thread-0进程已结束,退出代码0

通俗来说,假设我是经理,我把我的下属员工张三喊过来,我告诉张三他这周的工作任务现在,我让他行动起来.

在这段文字中:

  • "我是经理" → 你是主线程 (main 线程)。

  • "把张三喊过来" → 你创建了一个线程对象 (new Thread())。

  • "我告诉张三他的工作任务" → 你覆写了 run() 方法,定义了线程要执行的内容。

  • "我让张三行动起来"==start() → 你调用了 start() 方法,让张三真正开始工作,并且他是独立工作的,你自己(主线程)也可以去做别的事了。

  • "如果我只是告诉张三他的工作任务,而没让他行动"==run() → 你只是调用了 run() 方法,这样张三不会真正开始工作,而是你自己(经理)亲自去做他的工作(主线程执行 run() 里面的内容)。

Thread.join()(等待线程)

在 Java 线程编程中,join() 方法用于让当前线程等待另一个线程执行完成,然后才继续执行。它的作用是同步线程,确保某个线程执行完毕后,其他线程才能继续运行。

例如现在有一个线程t,如果主线程调用t.join(),那么主线程就会进入阻塞状态,等待t线程先执行完

笔者再给一个示例代码:

class Worker extends Thread {private String name;public Worker(String name) {this.name = name;}@Overridepublic void run() {System.out.println(name + " 开始工作...");try {Thread.sleep(2000);  // 模拟任务执行 2 秒} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name + " 工作完成!");}
}public class JoinExample {public static void main(String[] args) {Worker worker1 = new Worker("张三");Worker worker2 = new Worker("李四");worker1.start();worker2.start();try {worker1.join();  // 等待 worker1 执行完worker2.join();  // 等待 worker2 执行完} catch (InterruptedException e) {e.printStackTrace();}System.out.println("所有工作完成,主线程继续执行!");}
}

 效果如下:

张三 开始工作...
李四 开始工作...
张三 工作完成!
李四 工作完成!
所有工作完成,主线程继续执行!

又或者,我们希望多个线程按照一定顺序进行,也可以使用join()方法

例如:

public class JoinExample {public static void main(String[] args) {Thread thread1 = new Thread(() -> {System.out.println("线程 1 执行");});Thread thread2 = new Thread(() -> {System.out.println("线程 2 执行");});Thread thread3 = new Thread(() -> {System.out.println("线程 3 执行");});try {thread1.start();thread1.join();  // 等待 thread1 结束thread2.start();thread2.join();  // 等待 thread2 结束thread3.start();thread3.join();  // 等待 thread3 结束} catch (InterruptedException e) {e.printStackTrace();}System.out.println("所有线程执行完毕!");}
}

在main线程中那个线程调用了join()方法, main线程就一定要等待该线程执行完,然后才能接着执行

效果如下:

线程 1 执行
线程 2 执行
线程 3 执行
所有线程执行完毕!

感兴趣的读者们可以把代码拷到自己的环境中去尝试 !

3.线程的状态

我们如果想涉及多线程的安全问题, 那我我们就必须先知道多线程有哪些状态?

前面我们提到过 getState() 可以获取当前线程的状态

public class ThreadState {public static void main(String[] args) {// 遍历 Thread.State 枚举中的所有状态for (Thread.State state : Thread.State.values()) {// 输出每种状态System.out.println(state);}}
}
NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED进程已结束,退出代码0

1. NEW(新建)

  • 线程刚刚被创建,还 没有启动,此时线程对象已经被分配内存,但还未执行 start() 方法。

  • 特征:未执行任何代码。

Thread thread = new Thread(() -> System.out.println("Hello"));
System.out.println(thread.getState()); // NEW

2. RUNNABLE(运行中)

  • 线程已经 调用 start() 方法,并且 可能正在运行,也可能在等待 CPU 调度(就绪)。

  • 这个状态表示线程 可运行,但 不一定正在运行(因为可能在 CPU 时间片等待中)。

Thread thread = new Thread(() -> {while (true) {} // 无限循环,保持线程存活
});
thread.start();
System.out.println(thread.getState()); // RUNNABLE

3. BLOCKED(阻塞)

  • 线程 等待获取锁,但该锁 被其他线程持有,导致线程无法继续执行。

  • 特征只能用于同步代码块synchronized),等待进入临界区

4. WAITING(无限等待)

  • 线程 无限期等待,直到 被其他线程显式唤醒notify()notifyAll())。

  • 特征:不会自动恢复,必须 由其他线程唤醒

5. TIMED_WAITING(限时等待)

  • 线程进入 限时等待状态,会在指定时间后自动恢复

  • 特征:等待 超时后自动唤醒,无需其他线程干预。

6. TERMINATED(终止)

  • 线程执行完 run() 方法,或者 抛出未捕获异常 导致终止。

  • 线程生命周期结束,不能再 start()

结尾

写到这里,笔者就暂时停笔了,知识简单但是汇总不易,实验的结果都是笔者自己写代码自己试验出来的,希望对读者有用,可以的话,请您投个票

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

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

相关文章

Elasticsearch:人工智能时代的公共部门数据治理

作者:来自 Elastic Darren Meiss 人工智能(AI)和生成式人工智能(GenAI)正在迅速改变公共部门,从理论探讨走向实际应用。正确的数据准备、管理和治理将在 GenAI 的成功实施中发挥关键作用。 我们最近举办了…

使用事件监听器来处理并发环境中RabbitMQ的同步响应问题

RabbitListener 是 Spring AMQP 提供的核心注解,用于简化 RabbitMQ 消息监听器的创建。以下是对 RabbitListener(queues "balloonWords.queue") 的详细解析: 一、基础功能 队列监听 通过 queues 属性指定监听的队列名称(如 "…

2025年数智化电商产业带发展研究报告260+份汇总解读|附PDF下载

原文链接:https://tecdat.cn/?p41286 在数字技术与实体经济深度融合的当下,数智化产业带正成为经济发展的关键引擎。 从云南鲜花产业带的直播热销到深圳3C数码的智能转型,数智化正重塑产业格局。2023年数字经济规模突破53.9万亿元&#xff…

自动驾驶04:点云预处理03

点云组帧 感知算法人员在完成点云的运动畸变补偿后,会发现一个问题:激光雷达发送的点云数据包中的点云数量其实非常少,完全无法用来进行后续感知和定位层面的处理工作。 此时,感知算法人员就需要对这些数据包进行点云组帧的处理…

Servlet注解与使用模板方法设计模式优化oa项目

一、Servlet注解,简化配置 分析oa项目中的web.xml文件 现在只是一个单标的CRUD,没有复杂的业务逻辑,很简单的一丢丢功能。web.xml文件中就有如此多的配置信息。如果采用这种方式,对于一个大的项目来说,这样的话web.xml…

污水处理厂人员定位方案-UWB免布线高精度定位

1. 方案概述 本方案采用免布线UWB基站与北斗卫星定位融合技术,结合UWBGNSS双模定位工卡,实现污水处理厂室内外人员高精度定位(亚米级)。系统通过低功耗4G传输数据,支持实时位置监控、电子围栏、聚集预警、轨迹回放等功…

【C++初阶】第12课—list

文章目录 1. list的构造2. list迭代器的常见接口2.1 list遍历的迭代器接口2.2 list修改数据的迭代器接口2.3 list排序、逆序、合并相关操作的成员函数 3. 模拟实现list3.1 模拟实现list的构造3.2 模拟实现list的尾插3.3 模拟实现迭代器iterator3.4 模拟实现list的插入删除3.5 模…

Java进阶

Java进阶 注解什么是注解?内置注解元注解自定义注解 对象克隆(对象复制)如何实现克隆?浅克隆深克隆 设计模式统一建模语言(UML)类接口类之间的关系 面向对象设计原则1. 单一职责2. 开闭原则3. 里氏替换原则…

AB包介绍及导出工具实现+AB包资源简单加载

Resource原理 项目中建立Resources目录,资源导入内部 生成项目包 资源文件存储路径 结论:存储在Resources下的资源,最终会存储在游戏的主体包中,发送给用户,手机系统上,如果需要做资源的更新,是…

全包圆玛奇朵样板间亮相,极简咖啡风引领家装新潮流

在追求品质生活的当下,家居装修风格的选择成为了许多消费者关注的焦点。近日,全包圆家居装饰有限公司精心打造的玛奇朵样板间正式对外开放,以其独特的咖啡色系极简风格,为家装市场带来了一股清新的潮流。玛奇朵样板间不仅展示了全…

算法基础——模拟

目录 1 多项式输出 2.蛇形方阵 3.字符串的展开 模拟,顾名思义,就是题⽬让你做什么你就做什么,考察的是将思路转化成代码的代码能⼒。这类题⼀般较为简单,属于竞赛⾥⾯的签到题(但是,万事⽆绝对&#xff…

Java---类与对象

类与对象 前言:一、面向对象二、类的定义1.类的定义格式2.访问修饰限定符 三、类的实例化四、this引用1.this引用2.this引用的原因 五、对象的构造和初始化1.初始化对象2.构造方法(1).构造方法的概念:(2).特性:(3).this调用:3.就地初始化4.默…

【巧文书高效编标工具】技术解决方案

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 巧文书高效编标工具**是一款基于自然语言处理(NLP)与自动化技术的智能编标解决方案,旨在提升标书编制效率、降低人工错误率。通过结合规则引擎、模板化生成和AI辅助校对&#xff0c…

1.基于TCP的简单套接字服务器实现

目录 1. TCP通信流程 2. 服务器端的通信流程 2.1 创建用于监听的套接字 2.2 绑定本地IP地址和端口 2.3 设置监听 2.4 等待接受客户端的连接请求 2.5 与客户端进行通信 2.6 客户端连接服务器 3.代码实现 client.cpp server.cpp 运行方式 在本文中,我们将…

【大模型基础_毛玉仁】5.3 附加参数法:T-Patcher

目录 5.3 附加参数法:T-Patcher5.3.1 补丁的位置1)键值存储体2)补丁设计 5.3.2 补丁的形式5.3.3 补丁的实现1)准确性2)局部性 5.3 附加参数法:T-Patcher 附加参数法:通过引入可训练的额外参数实…

【原理系列】计算机组成原理-第一遍阅读总结

目录 创作灵感: 总览: 1、计算机抽象与技术 2、ISA指令集架构 3、CPU处理器 4、存储器 创作灵感: 夯实计算机原理,构建知识框架 此系列将总结和记录我对 【深入浅出计算机组成原理-台湾科技大学/元智大学的刘一宇教授】 的…

React程序打包与部署

===================== 推荐超级课程: 本地离线DeepSeek AI方案部署实战教程【完全版】Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战目录 为生产环境准备React应用最小化和打包环境变量错误处理部署到托管服务部署到Netlify探索高级主题:Hooks、Su…

C++运算符重载、类的转换构造函数和类型转换函数的基础练习

练习1:(困难) 建立一个矩阵类,可以完成指定的操作或运算。 说明: (1)、矩阵为2行3列,基类型为整型; (2)、操作或运算:初始化&…

PERL开发环境搭建>>Windows,Linux,Mac OS

特点 简单 快速 perl解释器直接对源代码程序解释执行,是一个解释性的语言, 不需要编译器和链接器来运行代码>>速度快 灵活 借鉴了C/C, Basic, Pascal, awk, sed等多种语言, 定位于实用性语言,既具备了脚本语言的所有功能,也添加了高级语言功能 开源.免费 没有&qu…

docker(2) -- 启动后修改目录和网络

1. 前言 docker启动前是image文件,启动后是container文件,启动的时候我们可以指定容器的挂载目录以及网络类型,但启动后,这些信息都以配置文件的形式保存在container中,container重新启动时无法重新指定这些信息&…