Java【多线程基础2】 Thread类 及其常用方法
创始人
2025-05-29 03:27:56

文章目录

  • 前言
  • 一、Thread类
    • 1, 构造方法
    • 2, 常用成员属性
    • 3, 常用成员方法
      • 3.1, start 启动线程
      • 3.2, interrupt 中断线程 (重点)
        • 3.2.1, 手动设置标记位
        • 3.2.2, 使用内置标记位
        • 3.3.3, interrupt 方法 的作用
      • 3.3 sleep 休眠线程
      • 3.4, jion 等待线程
      • 3.5 获取当前线程的引用
  • 总结


前言

各位读者好, 我是小陈, 这是我的个人主页
小陈还在持续努力学习编程, 努力通过博客输出所学知识
如果本篇对你有帮助, 烦请点赞关注支持一波, 感激不尽
希望我的专栏能够帮助到你:
JavaSE基础: 从数据类型类和对象, 封装继承多态, 接口, 综合小练习图书管理系统
Java数据结构: 顺序表, 链表, 二叉树, , 哈希表等 (正在持续更新)
JavaEE初阶: 多线程, 网络编程, html, css, js, severlet, http协议, linux等(正在持续更新)

上篇介绍了[多线程基础篇1], 主要内容 : 线程 的概念, 线程 和 进程 的区别, 以及如何创建线程
本篇继续介绍多线程相关的基础内容, 内容较多, 分为若干篇持续分享


提示:是正在努力进步的小菜鸟一只,如有大佬发现文章欠佳之处欢迎批评指点~ 废话不多说,直接上干货!

一、Thread类

Thread类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread类 的对象与之关联

每一个线程, 都需要被描述(具体被描述什么, 下面再说), Thread类 的对象就是用来描述一个线程的

学习一个类, 要从它的构造方法学起


1, 构造方法

1, 无参构造方法 :

在上一篇介绍线程的创建方式时, 使用到了匿名内部类继承Thread, 重写 run 方法的方式, 这就是无参构造法的使用

        Thread thread = new Thread() {@Overridepublic void run() {System.out.println("一个新的thread线程");}};

2, 一个参数的构造方法 : 参数是 String 类型的 name , 表示线程的名字

其实就是方法1 的创建线程方式, 构造方法参数可以传递一个"name", 唯一的作用只是程序员知道自己创建的线程是谁, 方便多线程中代码调试

		// 给这个线程命名为"喜羊羊"Thread thread = new Thread("喜羊羊") {@Overridepublic void run() {System.out.println("一个新的thread线程");}};thread.start();// 输出线程的名字System.out.println("thread线程的名字是: " +  thread.getName());

在这里插入图片描述

3, 一个参数的构造方法 : 参数是 Runnable 对象

在上一篇介绍线程的创建方式时, 使用到了lambda表达式的方式, 本质上就是匿名内部类实现了 Runnable 接口, 重写 run 方法, 把 Runnable 对象作为参数

        Thread thread = new Thread( () -> {while(true) {System.out.println("一个新的thread线程");}});

4, 两个参数的构造方法 : 一个是 Runnable 接口, 一个是 String 类型的 name

使用 lambda 表达式传参之后, 再传入一个字符串即可

        Thread thread = new Thread(() -> {System.out.println("一个新的thread线程");}, "美羊羊"); // 给这个线程命名为"美羊羊"thread.start();// 输出线程的名字System.out.println("thread线程的名字: " + thread.getName());

在这里插入图片描述


2, 常用成员属性

1, ID . 获取 ID 使用 getId()
表示线程的唯一身份表示, 不会重复

2, 名称 . 获取名称使用 getName()
表示线程的名字, 对代码调试有帮助

3, 状态 . 获取状态使用 getState()
表示线程所处的情况, 下面会详细讨论

4, 优先级 . 获取优先级使用 getPriority()
优先级高线程的理论上会被优先调度, 但是线程是操作系统进行调度执行的基本单位
所以线程被调度的顺序归根结底还是操作系统决定的

5, 是否为后台线程(默认为前台) . 获取是否为后台线程使用 isDaemon()
前台线程会阻止进程的结束, 后台线程不会, 啥意思?
java 进程中的所有前台线程都结束 java 进程才能结束, 后台线程不管是否结束, java 进程该结束就结束

可以通过 setDaemon() 把线程改为后台线程(true)

6, 是否存活 . 获取是否存活使用 isAlive()
run 方法结束了, 说明线程执行完了, 就不存在了(不存活了), 或者 run 方法执行之前, 线程也不存在(不存活)

7, 是否被中断 . 获取是否被中断使用 isInterrupted()
这个下面介绍 interrupt 方法时会介绍到

代码展示:

		Thread thread = new Thread(() -> {}, "美羊羊");System.out.println("-----调用 start 方法之前-----");System.out.println("thread线程 的状态 : " + thread.getState() + " (这是啥意思? 下面会介绍到)");System.out.println("thread线程 是否存活 : " + thread.isAlive() + " 此时 thread线程 不存在, 所以不存活");System.out.println(" ");thread.start();System.out.println("-----调用 start 方法时-----");System.out.println("thread线程 是否存活 : " + thread.isAlive() + " 此时 thread线程 正在执行, 所以不存活");System.out.println(" ");System.out.println("-----调用 start 方法之后-----");// 1, 获取thread线程的IDSystem.out.println("1, thread线程 的 id : " + thread.getId());// 2, 获取thread线程的名字System.out.println("2, thread线程 的名字 : " + thread.getName());// 3, 获取thread线程的状态System.out.println("3, thread线程 的状态 : " + thread.getState() + " (这是啥意思? 下面会介绍到)");// 4, 获取thread线程的优先级System.out.println("4, thread线程 的优先级 : " + thread.getPriority());// 5, thread线程是否为后台线程System.out.println("5, thread线程 是否为后台线程 : " + thread.isDaemon() + "默认都是false");// 6, thread线程是否存活System.out.println("6, thread线程 是否存活 : " + thread.isAlive() + " 此时 thread线程 已经执行完了, 所以不存活");// 7, thread线程是否被中断System.out.println("7, thread线程 是否被中断 : " + thread.isInterrupted());

在这里插入图片描述


3, 常用成员方法

3.1, start 启动线程

这个方法已经使用过很多次了, 再做一些强调 :

重写了 run 方法只是用代码描述了线程要执行什么操作
上篇介绍了很多方式用来创建线程对象, 创建了线程对象不代表线程开始执行
程序员自己调用 run 方法虽然也会执行方法体中的代码, 但并没有启动新的线程
调用 start 方法才会启动一个线程, 这个被创建出来的线程才会真正独立执行


3.2, interrupt 中断线程 (重点)

3.2.1, 手动设置标记位

如果在 run 方法中执行循环打印, 循环条件为 true , 那么调用 start 方法启动线程后, 这个线程将无法结束

如果我们提前设置一个标记位 flag 为 false, 循环条件为 !flag , 并且每执行一次打印, 就休眠 1000 毫秒, 此时线程仍无法结束

使用 sleep 方法需要使用 try-catch 语句捕获一个 InterruptedException异常, 表示休眠期间被打断就会抛出异常
catch 语句中的 e.printStackTrace(); 就是在出现异常时用来打印调用栈信息

	// 成员属性 flagprivate static boolean flag = false;public static void main(String[] args)  {Thread thread = new Thread(() -> {while(!flag) {System.out.println("一个新的thread线程");try {// 打印一次, 休眠 1 秒 Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});

然后, 令主线程休眠 3000 毫秒后把 flag 设置为 true,

        thread.start();try {Thread.sleep(3000);flag = true;} catch (InterruptedException e) {e.printStackTrace();}

此时 thread 线程中的循环不满足循环条件, 就会结束, 主线程 和 thread 线程的并发执行结果如下 : 在这里插入图片描述


3.2.2, 使用内置标记位

上面是我们手动设置了标记位
而 Thread类 的常见属性中提到的 isInterrupted(), 就是内置的标记位, 默认为 false , 类似于刚刚代码里的 flag, 我们用 isInterrupted() 替换上面的 flag :

			// 省略其他代码while(!Thread.currentThread().isInterrupted()) {// 省略其他代码

Thread . currentThread() 是获取当前线程的示例, 也就是 thread
所以 : Thread . currentThread() . isInterrupted() 等同于 thread . isInterrupted()
只不过此时还没有完成对 thread 的初始化, 不能如后者那样写

接下来主线程中如何修改标记为呢? 就是使用 interrupt 方法 直接中断线程

		// 省略其他代码try {Thread.sleep(3000);thread.interrupt();} catch (InterruptedException e) {e.printStackTrace();}

程序会如何执行呢? 需要先看看 interrupt 方法 到底有什么用


3.3.3, interrupt 方法 的作用

1, 会把标志位设为 true, 执行完 interrupt 之后 isInterrupted 的值就被设为 true 了
2, 如果线程处于阻塞状态, 就会取消阻塞状态, 上述代码中因为 sleep 方法, thread线程 会处于阻塞态(休眠), interrupt 就会让休眠结束, sleep 抛出异常,

所以, 按理说, 这个并发执行的程序最终会以一个异常结束
在这里插入图片描述

可以看到, catch 语句捕获了异常, 说明线程确实被打断了, 可是为什么线程没有结束, 还在循环打印呢 ? 原因是因为 sleep 方法


3.3 sleep 休眠线程

关于 sleep 方法的使用 以上代码已经用过很多次了, 这里再做个总结 :
参数为毫秒, 表示 sleep 方法执行后, 线程休眠多久
需要用 try-catch 捕获 InterruptedException 这个受查(编译时)异常, 如果休眠时被中断, 就会抛出异常

但是, sleep方法 还有一个重要操作 ! !
在休眠时如果被唤醒, sleep方法会自动把标志位清空: 设置成 false

这就导致了在上述代码中, 为啥调用了 interrupt 方法后, 线程没有真正被中断, 就是因为标志位又被设置成了 false , 所以程序仍然会执行循环

sleep 方法为什么会这么做呢?

原因是, Thread类 并不希望线程执行了 interrupt 方法之后就立即被中断, 这样是很霸道很强硬的做法
它希望的效果是, interrupt 方法, 仅仅起到一个 “提示” 或者 “通知” 的作用, 然后由程序员编写代码, 用代码逻辑来控制线程是立即结束, 还是等一会再结束, 还是无视这个 “通知”

比如我女朋友让我别刷抖音了, 陪她出门逛街, 我完全可以立即关掉抖音
但如果我此时在努力敲代码, 有很重要的学习任务, 我可以和她商量, 能不能等我学习完再去
如果我无论什么状态下都立即中断手头的事儿, 显然是不合理的

站在我的角度来说, 只有我自己最了解, 最关心自己, 知道现在应该做什么

站在程序员的角度上, 只有程序员最了解, 最关心自己写的代码, 知道当前的代码是否应该结束, 所以交给程序员决定何时中断才是最优解

那么在上述代码中, 如何实现立即中断呢? 只需要在捕获异常之后加一个 break 即可

				// 省略其他代码} catch (InterruptedException e) {e.printStackTrace();break;}// 省略其他代码

在这里插入图片描述


3.4, jion 等待线程

有的时候, 多个线程不能满足并发的条件, 可能需要等某个线程执行完再并发执行

例如我要和女朋友去约会, 我应该去她家楼下接她, 如果我到她家楼下后, 她还在化妆, 我应该等她下楼之后再一起去约会

我们先再复习一下多线程的并发执行效果 :

        Thread thread = new Thread( () -> {// 循环打印 5 次for (int i = 0; i < 5; i++) {System.out.println("thread线程");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});thread.start();// 循环打印 5 次for (int i = 0; i < 5; i++) {System.out.println("主线程");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}

在这里插入图片描述

两条打印语句交替执行, 如果我在调用 start 方法启动 thread线程 之后, 再写一个 thread.join(), 会发生什么呢?

		// 省略其他代码thread.start();thread.join();// 省略其他代码

在这里插入图片描述
可以看到, 当 thread线程 执行完了所有的循环打印之后, 主线程 才开始循环打印, 说明此时, 主线程 是等待 thread线程 结束之后才执行的

所以 : 主线程调用 thread.join 就是 主线程 等待 thread线程

3.5 获取当前线程的引用

上面已经使用过了, 这是一个静态方法, 会获取当前线程的引用, 然后可以继续进行其他操作

例如, 在 run 方法中执行完打印语句之后, 再打印一下当前线程的名字

        Thread thread = new Thread( () -> {for (int i = 0; i < 5; i++) {System.out.print("thread线程");System.out.println(" 我的名字叫 : " + Thread.currentThread().getName());}},"懒羊羊");thread.start();

在这里插入图片描述


总结

以上就是本篇的全部内容, 主要介绍了

Thread类中的 : 构造方法, 成员属性, 成员方法
其中, interrupt方法 和 sleep方法 有个特殊点, 需要重点理解

如果本篇对你有帮助,请点赞收藏支持一下,小手一抖就是对作者莫大的鼓励啦😋😋😋~


上山总比下山辛苦
下篇文章见

相关内容

热门资讯

考研数二第四讲 分段函数的复合... 分段函数的复合函数求分段函数的复合函数,这是考研高数中的一个重要考点。专升本的高数不考...
7座MPV车型推荐,7座MPV... 本篇文章极速百科给大家谈谈7座MPV车型推荐,7座MPV车型大全,以及7座mpv汽车大全2020对应...
支付宝怎么取消自动续费(苹果手... 今天给各位分享支付宝怎么取消自动续费的知识,其中也会对苹果手机支付宝怎么取消自动续费进行解释,如果能...
TH是什么意思(Things是... 本篇文章极速百科给大家谈谈TH是什么意思,以及Things是什么意思翻译对应的知识点,希望对各位有所...
上汽大众途观怎么样(上汽大众途... 本篇文章极速百科给大家谈谈上汽大众途观怎么样,以及上汽大众途观l2022版质量对应的知识点,希望对各...
solidworks转urdf... 是用solidworks成功导出了一次urdf,记录一下导出时各参数的说明。 基座的...
如何买火车票网上订票?网上买火... 今天给各位分享如何买火车票网上订票?网上买火车票怎么买的知识,其中也会对怎样买网上火车票进行解释,如...
加美机油质量怎么样?加美润滑油... 本篇文章极速百科给大家谈谈加美机油质量怎么样?加美润滑油排名第几,以及加美机油咋样对应的知识点,希望...
哈弗E2012款基本型配置-参... 今天给各位分享哈弗E2012款基本型配置-参数配置详解的知识,其中也会对哈弗二多少钱进行解释,如果能...
买拉法要满足什么条件(购买拉法... 今天给各位分享买拉法要满足什么条件的知识,其中也会对购买拉法的几条要求进行解释,如果能碰巧解决你现在...
JAVA并发编程之锁 1、乐观锁和悲观锁 1.1、悲观锁 认为自己在使用数据的时候一定有别的线程来修改数据,...
mysql数据库提权 0x00数据库帐号密码获取方式数据库帐号密码获取方式:1.网站存在高权限SQL注入点2...
橱窗男孩蔚来(橱窗小男孩看车壁... 今天给各位分享橱窗男孩蔚来的知识,其中也会对橱窗小男孩看车壁纸蔚来进行解释,如果能碰巧解决你现在面临...
包含铜雀春深锁二乔的典故是什么... 今天给各位分享铜雀春深锁二乔的典故是什么的知识,其中也会对进行解释,如果能碰巧解决你现在面临的问题,...
天津新地标津沽棒(天津新地标津... 本篇文章极速百科给大家谈谈天津新地标津沽棒,以及天津新地标津沽棒简介对应的知识点,希望对各位有所帮助...
meet的过去式是什么(see... 今天给各位分享meet的过去式是什么的知识,其中也会对see的过去式是什么进行解释,如果能碰巧解决你...
基于Hi3861平台的Open... 一、前言 本篇文章基于Hi3861平台的BearPi-HM_Nano开发板+E53IA1扩展板,进行...
中通面试题分享 redis有遇到过什么瓶颈 redis分布式锁怎么实现的,有哪些问题 布隆过滤器怎么实...
【Linux】GDB的安装与使... 安装安装gdb的具体步骤如下:1、查看当前gdb安装情况rpm -qa | grep ...
算法做题技巧:前缀和 什么是前缀 “前缀”是在计算机科学中广泛使用的一个数学术语。 从字面上解释,就是指一个...
家用轿车哪款比较好?家用轿车排... 本篇文章极速百科给大家谈谈家用轿车哪款比较好?家用轿车排行榜前十名2022,以及家用轿车排行榜202...
智能电表电量清零方法和智能电表... 今天给各位分享智能电表电量清零方法和智能电表故障分析及解决方法...的知识,其中也会对智能电表怎样复...
与中山公园有关的历史事件(中山... 今天给各位分享与中山公园有关的历史事件的知识,其中也会对中山公园故事进行解释,如果能碰巧解决你现在面...
上虞车辆违章查询系统官方入口(... 今天给各位分享上虞车辆违章查询系统官方入口的知识,其中也会对上虞区违章查询进行解释,如果能碰巧解决你...
记录--vue中封装一个右键菜... 这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 组件介绍 关于web...
xxl-job 的 API 接... 以下是使用 xxl-job 的 API 接口添加任务的 Java 源代码示例:impo...
【运维】运维常用命令 shell大全读取文件每一行内容文件是否存在数组定义和循环取值变量循环流程控制语句:c...
特斯拉降价引发新能源车市连锁反... 本篇文章极速百科给大家谈谈特斯拉降价引发新能源车市连锁反应,以及特斯拉降价背后的逻辑对应的知识点,希...
广东车辆违章查询系统官方入口(... 本篇文章极速百科给大家谈谈广东车辆违章查询系统官方入口,以及广东省车辆违章查询易车宝对应的知识点,希...
滴滴打车下架了吗?滴滴现在还能... 今天给各位分享滴滴打车下架了吗?滴滴现在还能用吗的知识,其中也会对滴滴打车已经下架了吗?进行解释,如...