Java【多线程基础2】 Thread类 及其常用方法
文章目录
- 前言
- 一、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方法 有个特殊点, 需要重点理解
如果本篇对你有帮助,请点赞收藏支持一下,小手一抖就是对作者莫大的鼓励啦😋😋😋~
上山总比下山辛苦
下篇文章见
相关文章:

Java【多线程基础2】 Thread类 及其常用方法
文章目录前言一、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 获取当前线程的引用总结前言 各位读…...
JVM调优实战及常量池详解
目录 阿里巴巴Arthas详解 Arthas使用场景 Arthas使用 GC日志详解 如何分析GC日志 CMS G1...

ChatGPT研究分析:GPT-4做了什么
前脚刚研究了一轮GPT3.5,OpenAI很快就升级了GPT-4,整体表现有进一步提升。追赶一下潮流,研究研究GPT-4干了啥。本文内容全部源于对OpenAI公开的技术报告的解读,通篇以PR效果为主,实际内容不多。主要强调的工作…...

我为什么要写博客,写博客的意义是什么??
曾经何时我也不知道,怎样才能变成我自己所羡慕的大佬!!在一次次的CSDN阅读的过程中,结实了许多志同道合的人!!包过凉哥,擦姐……大佬,但是,很遗憾,与这些人只…...

ssm框架之spring:浅聊AOP
AOP(Aspect Oriented Programming),是一种设计思想。先看一下百度百科的解释: 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态…...
k8s详解
一、k8s中的yaml文件 JSON格式:主要用于api接口之间信息的传递YAML格式:主要用于配置和管理,YAML是一种简洁的非标记性语言,内容格式人性化 YAML格式: 大小写敏感使用缩进代表层级关系,不支持TAB制表符缩…...
计算机操作系统(第四版)第一章操作系统引论 1.1操作系统的目标和作用
第一章操作系统引论 1.1操作系统的目标和作用 什么是操作系统OS? 配置在计算机硬件上的第一层软件是对硬件的首次扩充。 是最重要的系统软件,其他系统软件应用软件都依赖于操作系统的支持。 操作系统主要作用? 管理计算机系统所有硬件设…...
git push解决办法: ! [remote rejected] master -> master (pre-receive hook declined)
项目经理远程创建了一个空项目,无任何内容,给我赋予的developer账号权限,本地改为后提交代码试了很多次都上传不上去,报错如下: ! [remote rejected] master -> master (pre-receive hook declined)先说结果&#x…...

jQuery 遍历方法总结
遍历方法有:1、add(),用于把元素添加到匹配元素的集合中;2、children(),用于返回被选元素的所有直接子元素;3、closest(),用于返回被选元素的第一个祖先元素;4、contents(),用于返回…...
OKHttp 源码解析(二)拦截器
游戏SDK架构设计之代码实现——网络框架 OKHttp 源码解析(一) OKHttp 源码解析(二)拦截器 前言 上一篇解读了OKHttp 的基本框架源码,其中 OKHttp 发送请求的核心是调用 getResponseWithInterceptorChain 构建拦截器链…...

如何修改设置浏览器内核模式
优先级: 强制锁定极速模式 >手动切换(用户)>meta指定(开发者)>浏览器兼容列表(浏览器) 需要用360安全浏览器14,chromium108内核,下载地址https://bbs.360.cn/t…...
30个Python常用小技巧
1、原地交换两个数字 1 2 3 4 x, y 10, 20 print(x, y) y, x x, y print(x, y) 10 20 20 10 2、链状比较操作符 1 2 3 n 10 print(1 < n < 20) print(1 > n < 9) True False 3、使用三元操作符来实现条件赋值 [表达式为真的返回值] if [表达式] else [表达式…...
ubuntu解决中文乱码
1、查看当前系统使用的字符编码 ~$ locale LANGen_US LANGUAGEen_US: LC_CTYPE"en_US" LC_NUMERIC"en_US" LC_TIME"en_US" LC_COLLATE"en_US" LC_MONETARY"en_US" LC_MESSAGES"en_US" LC_PAPER"en_US" …...
2022年全国职业院校技能大赛(中职组)网络安全竞赛试题——MYSQL安全测试解析(详细)
B-3任务三:MYSQL安全测试 *任务说明:仅能获取Server3的IP地址 1.利用渗透机场景kali中的工具确定MySQL的端口,将MySQL端口作为Flag值提交; 2.管理者曾在web界面登陆数据库,并执行了select <?php echo \<pre>\;system($_GET[\cmd\]); echo \</pre>\; ?…...
C++ map和unordered_map的区别
unordered_map 类模板和 map 类模板都是描述了这么一个对象:它是由 std::pair<const Key, value> 组成的可变长容器; 这个容器中每个元素存储两个对象,也就是 key - value 对。 1. unordered_map 在头文件上,引入 <unor…...
BCSP-玄子JAVA开发之JAVA数据库编程CH-04_SQL高级(二)
BCSP-玄子JAVA开发之JAVA数据库编程CH-04_SQL高级(二) 4.1 IN 4.1.1 IN 子查询 如果子查询的结果为多个值,就会导致代码报错解决方案就是使用 IN 关键字,将 替换成 IN SELECT …… FROM 表名 WHERE 字段名 IN (子查询);4.1.…...

学习java——②面向对象的三大特征
目录 面向对象的三大基本特征 封装 封装demo 继承 继承demo 多态 面向对象的三大基本特征 我们说面向对象的开发范式,其实是对现实世界的理解和抽象的方法,那么,具体如何将现实世界抽象成代码呢?这就需要运用到面向对象的三大…...

初阶数据结构 - 【单链表】
目录 前言: 1.概念 链表定义 结点结构体定义 结点的创建 2.链表的头插法 动画演示 代码实现 3.链表的尾插 动画演示 代码实现 4.链表的头删 动画演示 代码实现 5.链表的尾删 动画演示 代码实现 6.链表从中间插入结点 动画演示 代码实现 7.从单…...

第五周作业、第一次作业(1.5个小时)、练习一
一、创建servlet的过程没有太多好说的,唯一需要注意的就是:旧版本的servlet确实需要手动配置web.xml文件,但是servlet2.5以后,servlet的配置直接在Java代码中进行注解配置。我用的版本就不再需要手动去配置web.xml文件了,所以我只…...

【正点原子FPGA连载】 第三十三章基于lwip的tftp server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南
第三十三章基于lwip的tftp server实验 文件传输是网络环境中的一项基本应用,其作用是将一台电子设备中的文件传输到另一台可能相距很远的电子设备中。TFTP作为TCP/IP协议族中的一个用来在客户机与服务器之间进行文件传输的协议,常用于无盘工作站、路由器…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...