多线程的学习中篇上

获取当前线程引用
| 方法 | 说明 |
|---|---|
| public static Thread currentThread(); | 返回当前线程对象的引用 |
currentThread() => 在那个线程中, 就能获取到那个线程的实例.
static关键字
static修饰的方法~~字面的直译 ~~静态方法(不好的翻译) => 类方法(更好的说法)
调用这个方法,不需要实例,直接通过类名来调用.
Thread t = new Thread;
调用这个上述这个方法的时候,直接Thread.currentThread();
不一定非要t.currrentThread;
从字面上看,static这个词,和类方法/类属性之间,没什么联系.
static ~~ 历史遗留问题
Java这么设计,是因为C++也是这么设计的. ~~ Java很多语法都是借鉴C++的
而C++又是从C语言演化来的,在C语言中,static可以修饰全局/变量变量,修饰函数.
~~ 在早期的操作系统中,内存划分的区域,是存在一个“静态内存区”.
static最初的作用就是被static修饰的变量就在“静态内存区”.
但是后来“静态内存区”没有了,但是static关键字被保留下来了,并重新赋予了新的功能.
C++中引入了类和对象,就急需有一种途径来表示"类属性"“类方法”.
就直接把之前的 static关键字给拿过来,不管它字面含义如何,直接赋予它新的历史使命了.
新的问题来了,为什么不创建一个新的关键字来表示“类属性,类方法”,比如Python中的classmethod?
原因就是关键字有特殊的含义不能作为变量名,不能作为函数名,不能作为类名……
一旦引入了新的关键字,此时原有写的千千万万写的代码里面一旦使用了这个关键字的单词作为
变量名,代码就无法通过编译器的编译了.
但是,这对于程序猿来说是无法接受的,那么多编译不过的代码是不可能重写的.
这个事情主要体现的就是“代码的兼容性”.
注:我们现在学的C语言的功能已经是变了之后的.
冷知识:C语言这近二十年,都没有太大的变化.C标准委员会大佬们已经在摆烂了,
包括C语言之父,肯汤姆逊,已经去全身心投身于Go语言了(不在更新,就是消亡的前兆).
休眠当前线程
~~ 本质上就是让这个线程不参与调度了(不去CPU上执行了)
| 方法 | 说明 |
|---|---|
| public static void sleep(long millis) throws InterruptedException | 休眠当前线程 millis 毫秒 |
| public static void sleep(long millis, int nanos) throws InterruptedException | 可以更高精度的休眠 |

注:
(1)PCB 是使用链表来组织的.(并不具体)实际的情况并不是一个简单的链表,其实这是一系列以链表为核心的数据结构.
(2)操作系统每次需要调度一个线程去执行,就从就绪队列中选一个就好了
(3)一旦线程进入阻塞状态,对应PCB就进入阻塞队列了,此时就暂时无法参与调度了.
(4)这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间的。
比如调用sleep(1000),对应的线程PCB就要再阻塞队列中待1000ms这么久
当这个PCB回到了就绪队列,会被立即调度嘛?
虽然是 sleep (1000),但是实际上考虑到调度的开销,对应的线程是无法在唤醒之后立即就执行的.实际上的时间间隔大概率要大于1000ms.
~~ 挂起(hung up)就是阻塞(block),一个意思,学校操作系统的课一般使用“挂起”这个术语较多.
线程的状态
状态是针对当前的线程调度的情况来描述的.
由于线程是调度的基本单位,状态就是线程的属性
| 属性 | 说明 | 个人理解 |
|---|---|---|
| 1.NEW | 安排了工作, 还未开始行动 | 创建了Thread 对象,但是还没调用start(内核里还没创建对应PCB) |
| 2.TERMINATED | 工作完成了 | 表示内核中的PCB已经执行完毕了,但是Thread对象还在 |
| 3.RUNNABLE | 可工作的. 又可以分成正在工作中和即将开始工作 | 可运行的(叫做RUNNABLE,而是RUNNING) (1)正在CPU上执行的 (2)在就绪队列里,随时可以去CPU上执行 |
| 4,5,6都是表示线程PCB正在阻塞队列中 | 4,5,6这三个状态是不同原因的阻塞 | 4,5,6这三个都表示排队等着其他事情 |
| 4.WAITING | 表示排队等着其他事情 | |
| 5.TIMED_WAITING | 表示排队等着其他事情 | |
| 6.BLOCKED | 表示排队等着其他事情 |
注:线程的状态是一个枚举类型 Thread.State, 可通过
for (Thread.State state : Thread.State.values()) { System.out.println(state); } 这段代码将线程的属性都打印出来.
线程状态和状态转移的意义
图示详解

博主所做的简化图

TERMINATED:
一旦内核里的线程PCB 消亡了,此时代码中t对象也就没啥用了
Java中的对象的生命周期和系统内核里的线程并非完全一致
内核的线程释放的时候,无法保证Java代码中t对象也立即释放
因此,势必就会存在,内核的PCB没了,但是代码中的t还在这样的情况
此时就需要通过特定的状态,来把t对象标识成“无效".
也是不能重新start 的,一个线程,只能start一次!!
~~ 一个变量/一个对象,一般只有一个用途(约定俗成的规矩)
示范代码:
public class ThreadDemo11 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{for (int i = 0; i < 1000000; i++) {// 这个循环体什么都不做}});// 启动之前, 获取 t 的状态, 就是 NEW 状态System.out.println("start 之前: "+t.getState());t.start();System.out.println("线程 t 执行中的状态: "+ t.getState());t.join();// 线程执行完毕之后, 就是 TERMINATED 状态System.out.println("线程 t 结束之后: "+ t.getState());}
}
运行结果:

之所以,此处能看到RUNNABLE,主要就是因为当前线程run里面,没写任何 sleep之类的方法.
当前获取到的状态,到底是什么,完全取决于系统的调度操作.
多线程的意义到底是什么?

多线程,在这种CPU密集型的任务中,有非常大的作用,可以充分利用CPU的多核资源
从而加快程序的运行效率.
多线程在IO密集型的任务中,也是很有作用的.
~~ 我们用智能手机的时候,有时候会遇到“程序未响应”的界面提示(比如打开QQ音乐的时候),
程序进行了一些耗时的IO操作(比如QQ音乐启动要加载数据文件,就涉及到大量的读硬盘操作),阻塞了界面的响应.这种情况下使用多线程也是可以有效改善的(一个线程负责IO,另一个线程用来响应用户的操作).
单线程写法:
public class ThreadDemo12 {public static void main(String[] args) {// 假设当前有两个变量, 需要把两个变量各自自增 100亿次. => 典型的 CPU 密集型的场景// 可以一个线程, 先针对 a 自增, 然后再针对 b 自增// 还可以两个线程, 分别是 a 和 b 自增serial();}// serial (串行) ~~ 串行执行, 一个线程完成public static void serial() {// 为了衡量代码的执行速度, 加上个计时的操作// currentTimeMillis 获取到当前系统的 ms 级时间戳long beg = System.currentTimeMillis();long a = 0;for (long i = 0; i < 100_0000_0000L; i++) {a++;}long b = 0;for (long j = 0; j < 100_0000_0000L; j++) {b++;}long end = System.currentTimeMillis();System.out.println("执行时间: " + (end - beg) + " ms");}
}

单线程写法的运行结果:

注: 就像这种衡量执行时间的代码,让他运行的久一点,不是坏事,运行的久,误差就小.
(线程调度自身也是有时间开销的)运算的任务量越大,线程调度的开销相比之下就特别不明显了,从而就可以忽略不计.
多线程写法:
package thread;/*** Created with IntelliJ IDEA.* Description:* User: fly(逐梦者)* Date: 2023-09-22* Time: 11:45*/// 通过这个线程来演示, 多线程和单线程相比, 效率的提升
public class ThreadDemo12 {public static void main(String[] args) {// 假设当前有两个变量, 需要把两个变量各自自增 100亿次. => 典型的 CPU 密集型的场景// 可以一个线程, 先针对 a 自增, 然后再针对 b 自增// 还可以两个线程, 分别是 a 和 b 自增//serial();concurrency();}// concurrency(并发性)public static void concurrency() {// 使用两个线程分别完成自增.Thread t1 = new Thread(() -> {long a = 0;for (long i = 0; i < 100_0000_0000L; i++) {a++;}});Thread t2 = new Thread(()->{long b = 0;for (long j = 0; j<100_0000_0000L;j++){b++;}});// 开始计时long beg = System.currentTimeMillis();t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}// 结束计时long end = System.currentTimeMillis();System.out.println("执行时间: " + (end - beg) + " ms");}
}

多线程写法的运行结果:

代码执行顺序:
main线程先调用t1.start.
启动t1开始计算t1的同时,main再调用t2.start,
启动t2的同时,t1仍然再继续计算.同时main线程进入就t1.join,
此时main阻塞等待了,t1和t2还是再继续执行的.
等到t1执行完了, main线程从t1.join返回,再进入t2.join,再来等待,
等到 t2执行完了,main 从t2.join返回,继续执行计时操作并打印执行时间.
~~ t2 是有可能比 t1 先结束的, 但是对程序结果没有任何影响.
t1.join 还是正常等,执行到 t2.join 的时候,就立即返回,不在等了.
此处使用两个线程并发执行,时间确实缩短的很明显(4916ms -> 2491ms)
为什么使用多线程更快?
多线程可以更充分的利用到多核心cpu的资源~~
但是为啥不是正好缩短一半??
(1)t1和t2,不一定是分布在两个CPU上执行,
它俩既有可能是并行执行,也有可能是并发执行.
实际上, t1和t2在执行过程中,会经历很多次的调度.
这些调度,有些是并发执行的(在一个核心上),有些是并行执行的(正好在两个核心上).
(2)线程调度自身也是有时间消耗的.
虽然虽然缩短的不是50%,但是仍然很明显,仍然很有意义!!
学习多线程推荐看的书:
<<Java并发编程的艺术>>, <<Java多线程核心技术>>
常见的开发方向:
1.后端开发
2.前端开发
3.客户端开发
4.测试开发
5.运维开发
6.算法工程师(策略开发)
7.游戏开发
……
多线程当前学习阶段的小结

相关文章:
多线程的学习中篇上
终其一生,满是遗憾 知足且坚定,温柔且上进 总之岁月漫长,然而值得等待 获取当前线程引用 方法说明public static Thread currentThread();返回当前线程对象的引用 currentThread() > 在那个线程中, 就能获取到那个线程的实例. static关键…...
非标准化套利
交易对象:目前使用非标准化组合进行交易。(即黄金远近月,焦煤焦炭等等) 交易平台:易盛极星极星产品网 手续费研究:白糖期货手续费和保证金2023年09月更新 - 九期网 本人使用的期货交易公司:中信期货&…...
从CNN(卷积神经网络),又名CAM获取热图
一、说明 卷积神经网络(CNN)令人难以置信。如果你想知道它如何看待世界(图像),有一种方法是可视化它。 这个想法是,我们从最后的密集层中得到权重,然后乘以最终的CNN层。这需要全局平均…...
kafka消费者多线程开发
目录 前言 kafka consumer 设计原理 多线程的方案 参考资料 前言 目前,计算机的硬件条件已经大大改善,即使是在普通的笔记本电脑上,多核都已经是标配了,更不用说专业的服务器了。如果跑在强劲服务器机器上的应用程序依然是单…...
布局设计和实现:计算器UI【TableLayout、GridLayout】
一、使用TableLayout实现计算器UI 1.新建一个空白项目布局 根据自己的需求输入其他信息 填写完成后,点击Finish即可 2. 设计UI界面 在res/layout文件夹中的XML文件中创建UI界面。在这个XML文件中,您可以使用TableLayout来设计计算器界面。 2.1 创建l…...
stack与queue的简单封装
前言: stack与queue即栈和队列,先进后出/先进先出的特性我们早已了然于心, 在学习数据结构时,我们利用c语言实现栈与队列,从结构体写起,利用数组或指针表示他们的数据成员,之后再一个个实现他们…...
ChatGPT使用技巧整理
目录 1. 让ChatGPT扮演专家角色2. 告诉ChatGPT你的身份3. 限制ChatGPT的回答长度4. 让ChatGPT一步步思考5. 明确你的要求和目的6. 提供充分的背景信息7. 始终结构化思考你的prompt1. 让ChatGPT扮演专家角色 当你们讨论的是市场营销问题时,你可以要求ChatGPT扮演一个具有20年从…...
机器学习笔记 - 维度诅咒的数学表达
1、点之间的距离 kNN分类器假设相似的点也可能有相同的标签。但是,在高维空间中,从概率分布中得出的点往往不会始终靠近在一起。 我们可以用一个简单的例子来说明这一点。 我们将在单位立方体内均匀地随机绘制点(如图所示),并研究该立方体内测试点的 k 个最近邻将占用多少…...
组合计数训练题解
CF40E 题目链接 点击打开链接 题目解法 首先,如果 n , m n,m n,m 一奇一偶,那么答案为 0 0 0 原因是从行和列的角度分析, − 1 -1 −1 个数的奇偶性不同 可以发现 k < max { n , m } k<\max\{n,m\} k<max{n,m} 的性质很微…...
P1095 [NOIP2007 普及组] 守望者的逃离
[NOIP2007 普及组] 守望者的逃离 - 洛谷 首先DP的套路就是先找状态 这题也找不出其他的状态了,只有时间一个 所以用f[i]表示时刻i能走多远 而仔细一想实际上决策只有跑、闪现、停三种决策 然而闪现的耗蓝要和跑步一同计算十分麻烦 于是把它们分开算࿱…...
Python函数绘图与高等代数互融实例(八):箱线图|误差棒图|堆积图
Python函数绘图与高等代数互融实例(一):正弦函数与余弦函数 Python函数绘图与高等代数互融实例(二):闪点函数 Python函数绘图与高等代数互融实例(三):设置X|Y轴|网格线 Python函数绘图与高等代数互融实例(四):设置X|Y轴参考线|参考区域 Python函数绘图与高等代数互融实例(五…...
联想y7000 y7000p 2018/2019 不插电源 不插充电器, 直接关机 ,电量一直89%/87%/86%,V0005如何解决?
这种问题,没有外力破坏的话,电池不可能突然出事。这种一般是联想的固件问题,有可能发生在系统更新,或者突然的不正常关机或长时间电池过热,原因我不是很清楚。 既然发生了,根据我收集的解决方法,…...
stm32与esp8266通信
esp8266 #include <ESP8266WiFi.h> #include <ESP8266HTTPClient.h>// 测试HTTP请求用的URL // #define URL "http://162.14.107.118:8086/PC/modifyFoodPrice/0/6"// 测试HTTP请求用的URL // 设置wifi接入信息(请根据您的WiFi信息进行修改) const char…...
组合数 2.1 2.2
O(nlogn)预处理, O(1)查询 #include<bits/stdc.h> #define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); #define endl \nusing namespace std;typedef pair<int, int> PII; typedef long long ll; typedef long double ld;const int N 1000…...
【数组的中心位置】python实现-附ChatGPT解析
1.题目 数组的中心位置 题目 给你一个整数数组 nums,请计算数组的中心位置。 数组中心位置是数组的一个下标,其左侧所有元素相乘的积等于右侧所有元素相乘的积。 数组第一个元素的左侧积为 1,最后一个元素的右侧积为 1。 如果数组有多个中心位置,应该返回最靠近左边的那一个…...
黑马JVM总结(二十三)
(1)字节码指令-init 方法体内有一些字节,对应着将来要由java虚拟机执行方法内的代码,构造方法里5个字节代码,main方法里有9个字节的代码 java虚拟机呢内部有一个解释器,这个解释器呢可以识别平台无关的字…...
AI人体行为分析:玩手机/打电话/摔倒/攀爬/扭打检测及TSINGSEE场景解决方案
一、AI人体行为分析技术概述及场景 人体姿态分析/行为分析/动作识别AI算法,是一种利用人工智能技术对人体行为进行检测、跟踪和分析的方法。通过计算机视觉、深度学习和模式识别等技术,可以实现对人体姿态、动作和行为的自动化识别与分析。 在场景应用…...
HI_NAS linux 记录
dev/root 100% 占用解决记录 通过下面的命令查看各文件夹 大小 sudo du --max-depth1 -h # 统计当前文件夹下各个文件夹的大小显示为M 最终发现Var/log 占用很大空间 发现下面两个 log 占用空间很大,直接 rm-rf 即可 HI NAS python3 记录 # 安装pip3 sudo apt u…...
计算机图形学中的几何光学
文章目录 前言一、图形学中的光学二、光照模型1、经验型(简单)2、物理型(复杂) 前言 在学习Shader光照之前了解一下计算机图形学 一、图形学中的光学 镜面反射的效果例子:物体表面高光 慢反射的效果的例子:…...
「UG/NX」BlockUI 选择小平面区域 Select Facet Region
✨博客主页何曾参静谧的博客📌文章专栏「UG/NX」BlockUI集合📚全部专栏「UG/NX」NX二次开发「UG/NX」BlockUI集合「VS」Visual Studio「QT」QT5程序设计「C/C」C/C程序设计「Win」Windows程序设计「DSA」数据结构与算法「File」数据文件格式 目录 控件说…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
