多线程(三):Thread 类的基本属性
上一个篇章浅浅了解了一下 线程的概念,进程与线程的区别,如何实现多线程编程。
而且上一章提到一个重要的面试点: start 方法和 run 方法的区别。
start 方法是从系统那里创建一个新的线程,这个线程会自动调用内部的run 方法;
run 方法是一个线程的执行入口,所有的实现逻辑都写在该方法里。
从概念上来说,这两个方法是属于线程的属性。我们接着这个线程属性继续来认识线程。
线程属性
就像我们之前写的代码:

我们如何让某个线程中断呢?
这里的中断意味着:让线程停下来,线程的终止。
本质上来说让线程终止的办法只有一种,让线程的入口方法 执行完毕。无论是是 return 、抛出异常还是别的方法都是执行完毕。
这里就需要提到 给线程设定一个结束标志。
给线程设定一个结束标志
来看下面的这段代码:

我们可以选择这种方式来控制循环,不让他写的那么死。

但这仍是我们自己设置的变量,而不属于线程的属性,线程中也有个方法:interru方法
interru方法
我们来看看这一段代码:
我们这里就用到了几个线程的属性,
Thread.currentThread()、isInterrupted()、t.interrupt()
上述的e.printStackTrace() 就是打印出异常信息。
我们来看看结果:

我们发现,在main线程睡眠了 3s 之后并没有打印完异常就结束,而是继续线程 t 的运行。
这是为什么呢?
这就需要来聊聊 interrupt 方法的作用了:
- 将标识符设置为 true
- 如果该线程正在阻塞状态中(例如:sleep,后面还有其他方法也会使线程阻塞),会直接被interrupt 方法唤醒,此时通过抛出异常就会让 sleep 立即结束。
按照 interrupt 方法的作用,此时应该结束了啊,t 线程这么还会继续呢?
这里有个非常重要的点;
当 sleep 被唤醒以后,会自动将 isInterrupted 设置为 false,也就是说将异常标志位清空了(true -> false);
sleep 为什么要清空异常标志位呢?
目的是为了让这个线程能够对于线程什么时候结束有一个更精准的控制。
当前的 interrpt 方法是告诉线程你该结束了,至于什么时候结束,都是由代码灵活控制的。
interrpt 方法 是个通知而不是命令。
等待线程 -- join 方法
线程与线程之间的的并发执行的,线程间的调度是无序的,我们无法判断两个线程之间谁先结束谁后结束。
例如:

但是我们可以通过 join 方法进行控制,例如:

在t 执行 join 方法时,如果 t 线程尚未结束,那么main线程就会进入 Blocking (阻塞状态),因为t.join 在 main线程中执行。
main 线程代码走到这一行 就不参与 cpu 的执行调度了。
如果这个有多个线程并发执行, 那么 只是main 线程不参与线程的调度,其余的线程任然是抢占执行的。
我们使用的 join 是无参的版本,我们还存在另一个带参的版本。
这个带参的版本,参数作为等待的最大时间,超过了这个最大时间,t 线程还没有结束,那么main 线程 就会被唤醒,不再是Blocking 状态 就会继续执行。
而上面不带参的版本,就是死等,等不到 t 线程结束,mian 线程就不会开始。
既然上面都提到了线程状态,那么就简单理解一下线程状态。
线程状态
Java中线程的状态分为6种:
1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
3. 阻塞(BLOCKED):表示线程阻塞于锁。
4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
6. 终止(TERMINATED):表示该线程已经执行完毕。

我们简单理解就是一条主线,三条支线。
我们就简单了解一下就好,后面还会再聊到。
简单了解线程安全
对于多线程编程雀氏提供了很多便利,我们可以再同一时间完成多个任务,相比于多进程编程多线程还降低了 cpu 资源的占用。
这几章都提到了,多线程之间的线程调度是抢占式执行的,所以在此基础之上会产生很多的线程安全问题。
本章就稍稍介绍认识一下线程安全的问题就行。
本章以一个不安全的例子来认识
例如:我们设置一个 count ,两个线程各执行 5 w 次自增运算。
代码如下:
class Count {private int count = 0;public void add() {count++;}public int get() {return count;}
}
public class demo10 {public static void main(String[] args) throws InterruptedException {Count count = new Count();Thread t1 = new Thread(() -> {for (int i = 0; i < 50000; i++) {count.add();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 50000; i++) {count.add();}});t1.start();t2.start();t1.join();t2.join();System.out.println(count.get());}
}
运行一次看看结果:

再来试试看:
再来一次:

从结果上来看它是一个随机数。
至于为什么会产生一个随机数,我们需要了解在这执行的内部发生了什么事情:
对于这个 count++ 这个操作,本质上是由 cpu 上三个指令构成的:
- load : 从内存中将数据读取到 cpu 寄存器中
- add :在 cpu 中的值进行 + 1 运算
- save : 把寄存器中的值写入到内存中
由于是抢占式执行的,那么就会产生如下等问题:

这个 3 的执行顺序就变成不确定的了,产生多种排列组合结果。
只有 25 亿分之 1 的可能得到 10w 这个数字,也有可能会产生结果小于 5w 具体的可以自己排列组合看看。
对于 t1 和 t2 这两个线程可能式运行在同一个 cpu 核心上执行,也有可能在不同的 cpu 核心上执行。
归根结底,线程安全问题,全是因为线程之间的无序调度,导致了执行顺序不确定,结果不确定。
相关文章:
多线程(三):Thread 类的基本属性
上一个篇章浅浅了解了一下 线程的概念,进程与线程的区别,如何实现多线程编程。 而且上一章提到一个重要的面试点: start 方法和 run 方法的区别。 start 方法是从系统那里创建一个新的线程,这个线程会自动调用内部的run 方法&…...
蓝桥杯嵌入式第六课--串口收发
前言串口作为一个考试中考察频率较高的考点,其套路比较固定,因此值得我们仔细把握。本节课主要着眼于快速配置实现 串口收发与串口的中断。CubeMX配置选择串口2配置异步收发模式基本参数设置(波特率、校验位等等)开启串口收发中断…...
蓝桥杯冲刺 - Lastweek - 你离省一仅剩一步之遥!!!(掌握【DP】冲刺国赛)
文章目录💬前言🎯week3🌲day10-1背包完全背包多重背包多重背包 II分组背包🌲day2数字三角形 - 线性DP1015. 摘花生 - 数字三角形🌲day3最长上升子序列 - 线性DP1017. 怪盗基德的滑翔翼 - LIS1014.登山 - LIS最长公共子…...
C++ map与set的学习
1. 关联式容器在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、forward_list(C11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。关联式容器也…...
【C语言初阶】函数
文章目录💐专栏导读💐文章导读🌷函数是什么?🌷函数的分类🌺库函数🌺自定义函数🌷函数的参数🌷函数的调用🌷函数的嵌套调用和链式访问🌺嵌套调用&a…...
CentOS 7安装redis6.2.6(包括服务开机自启和开放端口)
CentOS 7安装redis6.2.61. 官网下载redis文件2. 校验安装依赖2.1 安装系统默认版本gcc2.2 升级gcc版本3. 解压编译安装4. 修改配置redis.conf4.2 设置密码4.3 绑定ip(可选)5. 启动redis服务并测试5.2 测试安装是否成功5.3 redis开机自启配置6.开放防火墙…...
基于注解的自动装配~
Autowired:实现自动装配功能的注解 Autowired注解能够标识的位置: 标识在成员变量上,此时不需要设置成员变量的set方法标识在成员变量对应的set方法上标识在为当前成员变量赋值的有参构造上使用注解进行自动装配,只要在其成员变量…...
【深度学习】【分布式训练】Collective通信操作及Pytorch示例
相关博客 【深度学习】【分布式训练】Collective通信操作及Pytorch示例 【自然语言处理】【大模型】大语言模型BLOOM推理工具测试 【自然语言处理】【大模型】GLM-130B:一个开源双语预训练语言模型 【自然语言处理】【大模型】用于大型Transformer的8-bit矩阵乘法介…...
Spring常用注解说明
目录 1.常用注解 2.特别说明 3.xml及注解方式 1.常用注解 (1) SpringBootApplication (2) ControllerRestControllerRequestMappingRequestParamPathVariableGetMappingPostMappingPutMappingDeleteMappingResponseBodyRequestBodyCrossOrigin (3) ConfigurationBeanServ…...
13-C++面向对象(纯虚函数(抽象类)、多继承、多继承-虚函数、菱形继承、虚继承、静态成员)
虚析构函数 存在父类指针指向子类对象的情况,应该将析构函数声明为虚函数(虚析构函数) 纯虚函数 纯虚函数:没有函数体且初始化为0的虚函数,用来定义接口规范 抽象类: 含有纯虚函数的类,不可以实…...
Android DataBinding 自定义View实现数据双向绑定
看不懂的可以先看看单向数据绑定:Android DataBinding数据变化时自动更新界面_皮皮高的博客-CSDN博客 然后再确定已经启动了dataBinding的情况下,按下面的顺序来: 首先创建一个自定义View: import android.content.Context imp…...
网络安全中的渗透测试主要那几个方面
渗透测试中主要有软件测试和渗透测试。 1、测试对象不同 软件测试:主要测试的是程序、数据、文档。 渗透测试:对象主要为网络设备、主机操作系统、数据库系统和应用系统。 2、测试内容不同 软件测试:主要工作内容是验证和确认,发…...
Cursor:GPT-4 驱动的强大代码编辑器
Cursor (https://www.cursor.so/)是 GPT-4 驱动的一款强大代码编辑器,可以辅助程序员进行日常的编码。下面通过一个实际的例子来展示 Cursor 如何帮助你编程。这个例子做的事情是网页抓取。抓取的目标是百度首页上的百度热搜,如下…...
C/C++中for语句循环用法及练习
目录 语法 下面是 for 循环的控制流: 实例 基于范围的for循环(C11) 随堂笔记! C语言训练-计算1~N之间所有奇数之和 题目描述 输入格式 输出格式 样例输入 样例输出 环形方阵 干货直达 for 循环允许您编写一个执行特定次数的循环的重复控制结构。…...
AnimatorOverrideController说明
unity-AnimatorOverrideControllerhttps://docs.unity.cn/cn/current/ScriptReference/AnimatorOverrideController.html 用于控制动画器重写控制器的接口。 动画器重写控制器的用途是重写某个控制器的动画剪辑,从而为给定化身定制动画。 在运行时基于相同的 Anim…...
1.4、第三阶段 MySQL数据库
root数据库技术 一、数据库理论 1 什么是数据库技术 数据库技术主要研究如何组织、存储数据,并如何高效地提取和处理数据。 2 什么是SQL SQL(Structured Query Language)结构化查询语言 SQL是操作数据库的命令集,也是功能齐全的…...
LeetCode:202. 快乐数
🍎道阻且长,行则将至。🍓 🌻算法,不如说它是一种思考方式🍀算法专栏: 👉🏻123 一、🌱202. 快乐数 题目描述:编写一个算法来判断一个数 n 是不是快…...
Android 14 新功能之 HighLights:快速实现文本高亮~
日常开发中可能会遇到给 TextView 的全部或部分文本增加高亮效果的需求,以前可能是通过 Spannable 或者 Html 标签实现。 升级 Android 14 后就不用这么迂回了,因其首次引入直接设置高亮的 API:HighLights。需要留意的是 HighLights API 和 …...
[渗透教程]-004-嗅探工具-Nmap
文章目录 Nmap介绍基本操作进阶操作Nmap介绍 nmap是一个网络扫描和主机检测工具,它可以帮助用户识别网络上的设备和服务。获取主机正在运行哪些服务,nmap支持多种扫描,UDP,TCP connect(),TCP SYN(半开扫描) ftp代理,反向标志,ICMP,FIN,ACK扫描,ftp代理,反向标志,ICMP. 可以用于…...
大数据技术之Hive SQL题库-初级
第一章环境准备1.1 建表语句hive>-- 创建学生表 DROP TABLE IF EXISTS student; create table if not exists student_info(stu_id string COMMENT 学生id,stu_name string COMMENT 学生姓名,birthday string COMMENT 出生日期,sex string COMMENT 性别 ) row format delim…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...
ubuntu22.04 安装docker 和docker-compose
首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...
Unity VR/MR开发-VR开发与传统3D开发的差异
视频讲解链接:【XR马斯维】VR/MR开发与传统3D开发的差异【UnityVR/MR开发教程--入门】_哔哩哔哩_bilibili...
Win系统权限提升篇UAC绕过DLL劫持未引号路径可控服务全检项目
应用场景: 1、常规某个机器被钓鱼后门攻击后,我们需要做更高权限操作或权限维持等。 2、内网域中某个机器被钓鱼后门攻击后,我们需要对后续内网域做安全测试。 #Win10&11-BypassUAC自动提权-MSF&UACME 为了远程执行目标的exe或者b…...
AWSLambda之设置时区
目标 希望Lambda运行的时区是东八区。 解决 只需要设置lambda的环境变量TZ为东八区时区即可,即Asia/Shanghai。 参考 使用 Lambda 环境变量...
