【JavaEE初阶】认识线程、创建线程
1. 认识线程(Thread)
1.1 概念
1) 线程是什么
一个线程就是一个 "执行流". 每个线程之间都可以按照顺序执行自己的代码. 多个线程之间 "同时" 执行着多份代码.
举例:
还是回到我们之前的银⾏的例⼦中。之前我们主要描述的是个⼈业务,即⼀个⼈完全处理⾃⼰的业务。我们进⼀步设想如下场景:⼀家公司要去银⾏办理业务,既要进⾏财务转账,⼜要进⾏福利发放,还得进⾏缴社保。如果只有张三⼀个会计就会忙不过来,耗费的时间特别⻓。为了让业务更快的办理好,张三⼜找来两位同事李四、王五⼀起来帮助他,三个⼈分别负责⼀个事情,分别申请⼀个号码进⾏排队,⾃此就有了三个执⾏流共同完成任务,但本质上他们都是为了办理⼀家公司的业务。 此时,我们就把这种情况称为多线程,将⼀个⼤任务分解成不同⼩任务,交给不同执⾏流就分别排队执⾏。其中李四、王五都是张三叫来的,所以张三⼀般被称为主线程(Main Thread)。
2) 为何要有线程
首先,我们来说一下进程。在多任务操作系统中,希望系统能够同时运行多个程序,这就引入了进程。如果是单任务的操作系统,就完全不涉及进程,也不需要管理(进程),更不需要调度。本质上说,进程是解决”并发编程“问题的,事实上,进程也可以很好地解决并发编程这样的问题。
但是在一些特定的环境下,进程的表现不尽人意,比如,有些场景下,需要频繁的创建和销毁进程,举例,最早的web开发,是使用C语言来编写的服务器程序(基于一种CGI这样的技术,其基于多进程的编程模式),服务器同一时刻会收到很多请求,针对每个请求,都会创建出一个进程,给这个请求提供一定的服务,返回对应的响应;一旦这个请求处理完了,此时这个进程就要销毁了。如果请求很多,就意味着服务器要不停地创建进程、销毁进程,此时使用多进程编程,系统的开销就会很大(开销主要体现在资源的申请和释放上)。
⾸先, "并发编程" 成为 "刚需"
- 单核 CPU 的发展遇到了瓶颈. 要想提⾼算力, 就需要多核 CPU,而并发编程能更充分利⽤多核 CPU资源
- 有些任务场景需要 "等待 IO", 为了让等待 IO 的时间能够去做⼀些其他的⼯作, 也需要⽤到并发编程
- 创建线程⽐创建进程更快.
- 销毁线程⽐销毁进程更快.
- 调度线程⽐调度进程更快.
多线程并发编程,效率更高,尤其是对于java进程,是要启动java虚拟机的,启动java虚拟机开销是更大的,搞多个java进程,就要多个java虚拟机。所以,java中不太去鼓励多进程编程。
3) 进程和线程的区别
- 进程是包含线程的. 每个进程⾄少有⼀个线程存在,即主线程。
- 进程和进程之间不共享内存空间. 同⼀个进程的线程之间共享同⼀个内存空间.
⽐如之前的多进程例⼦中,每个客户来银⾏办理各⾃的业务,但他们之间的票据肯定是不想让别⼈知道的,否则钱不就被其他⼈取⾛了么。⽽上⾯我们的公司业务中,张三、李四、王五虽然是不同的执⾏流,但因为办理的都是⼀家公司的业务,所以票据是共享着的。这个就是多线程和多进程的最⼤区别。
- 进程是系统分配资源的最小单位,线程是系统调度的最小单位。
- ⼀个进程挂了⼀般不会影响到其他进程. 但是⼀个线程挂了, 可能把同进程内的其他线程⼀起带⾛(整个进程崩溃).
另外注意:
- 同一个进程中的线程之间,可能会互相干扰,引起线程安全问题
- 线程也不是越多越好,要能够合适,如果线程太多了,调度开销可能会非常明显

4) Java 的线程 和 操作系统线程 的关系
操作系统内核,是操作系统最核心部分的功能模块(管理硬件、给软件提供稳定的运行环境)。操作系统 = 内核 + 配套的应用程序这里用银行为例来说明一下:当你到银行进行各种业务的办理的时候,都是需要在办事窗口前,给工作人员说清楚你的需求,由工作人员代办。我们知道银行中的办事窗口内部和银行大厅是分隔开的,你是进不去办事窗口内部的, 这里的办事窗口内部就相当于操作系统内核空间(内核态),你所在大厅则是用户空间(用户态)。
为什么划分出用户态、内核态:
最主要的目的,还是为了“稳定”。防止你的应用程序,把硬件设备或软件资源给搞坏了。系统封装了一些api,这些api都属于是一些“合法”的操作,应用程序只能调用这些api,这样就不至于对系统/硬件设备产生太大的危害。
假设让应用程序直接操作硬件,可能极端情况下,代码出现bug,就把硬件干烧了。
1.2 第⼀个多线程程序
- 每个线程都是⼀个独立的执行流
- 多个线程之间是 "并发" 执行的
class MyThread2 extends Thread {//Thread类不用导包,属于特殊的包java.long,该包默认自动导入@Overridepublic void run() {//run方法就是该线程的入口方法while (true) {System.out.println("hello run");try {Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}}
}
public class ThreadDemo2 {public static void main(String[] args) {//2、根据刚才的类,创建出具体的实例(线程实例,才是真正的线程)Thread t = new MyThread2();//3、调用Thread的start方法,才会真正调用系统api,在系统内核中创建出线程t.start();while (true) {System.out.println("hello main");try {Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}}
}
对于上述代码,运行结果为两个循环不停地同时输出(验证了多个线程之间是 "并发" 执行的)。
我们知道,若对于普通程序来说,当遇到一个无限循环,会停留在这个循环,不停的打印输出,后续的代码是执行不到的。然而这个多线程程序,两个循环都执行到了,是因为每个线程都是⼀个独立的执行流 。代码中 t.start() ,即调用start()之后会创建一个新的线程,该线程进入到 run 方法,进行循环;而此时main线程,这个主线程会继续自己的执行,执行后续代码,也进行循环。
这里可以使用 jconsole 命令观察线程:

2. 创建线程的几种方法
-
方法1 继承 Thread 类
我们上面写的第一个多线程程序就是用的该方法。
class MyThread extends Thread {@Overridepublic void run() {System.out.println("这⾥是线程运⾏的代码");}
} MyThread t = new MyThread(); t.start(); //调用start才会真正地创建线程 -
方法2 实现 Runnable 接口
1、实现 Runnable 接口
class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("这⾥是线程运⾏的代码");}
}
2、创建 Thread 类实例, 调用 Thread 的构造方法时将 Runnable 对象作为 target 参数.
Thread t = new Thread(new MyRunnable());//或者另一种写法
Runnable runnable = new MyRunnable();
Thread t = new Thread(runnable);
3、调用start方法
t.start(); // 线程开始运⾏
该方法完整代码示例:
class MyThread3 implements Runnable {@Overridepublic void run() {while (true) {System.out.println("hello runnable");try {Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}}
}
public class ThreadDemo3 {public static void main(String[] args) {
// Runnable runnable = new MyThread3();
// Thread t = new Thread(runnable);Thread t = new Thread(new MyThread3());t.start();while (true) {System.out.println("hello main");try {Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}}
}
- 继承 Thread 类, 直接使用 this 就表示当前线程对象的引用.
- 实现 Runnable 接口, this 表示的是 MyRunnable 的引用,需要使用Thread.currentThread()来表示当前线程对象
其他创建方法
-
匿名内部类创建 Thread 子类对象
// 使⽤匿名类创建 Thread ⼦类对象
Thread t1 = new Thread() {@Overridepublic void run() {System.out.println("使⽤匿名类创建 Thread ⼦类对象");}
};
-
匿名内部类创建 Runnable 子类对象
// 使⽤匿名类创建 Runnable ⼦类对象
Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("使⽤匿名类创建 Runnable ⼦类对象");}
});
-
lambda 表达式创建 Runnable 子类对象
// 使⽤ lambda 表达式创建 Runnable ⼦类对象
Thread t3 = new Thread(() -> System.out.println("使⽤匿名类创建 Thread ⼦类对象"));
Thread t4 = new Thread(() -> {System.out.println("使⽤匿名类创建 Thread ⼦类对象");
});
3. 多线程的优势-增加运行速度
- 使用 System.nanoTime() 可以记录当前系统的 纳秒 级时间戳.
- serial 串行的完成⼀系列运算. concurrency 使用两个线程并行的完成同样的运算.
public class ThreadAdvantage {// 多线程并不⼀定就能提⾼速度,可以观察,count 不同,实际的运⾏效果也是不同的private static final long count = 10_0000_0000;public static void main(String[] args) throws InterruptedException {// 使⽤并发⽅式concurrency();// 使⽤串⾏⽅式serial();}private static void concurrency() throws InterruptedException {long begin = System.nanoTime();// 利⽤⼀个线程计算 a 的值Thread thread = new Thread(new Runnable() {@Overridepublic void run() {int a = 0;for (long i = 0; i < count; i++) {a--;}}});thread.start();// 主线程内计算 b 的值int b = 0;for (long i = 0; i < count; i++) {b--;}// 等待 thread 线程运⾏结束thread.join();// 统计耗时long end = System.nanoTime();double ms = (end - begin) * 1.0 / 1000 / 1000;System.out.printf("并发: %f 毫秒%n", ms);}private static void serial() {// 全部在主线程内计算 a、b 的值long begin = System.nanoTime();int a = 0;for (long i = 0; i < count; i++) {a--;}int b = 0;for (long i = 0; i < count; i++) {b--;}long end = System.nanoTime();double ms = (end - begin) * 1.0 / 1000 / 1000;System.out.printf("串⾏: %f 毫秒%n", ms);}
}
该篇是对多线程的初步认识,接下来我会继续更新多线程的相关内容,请多多关注!
相关文章:
【JavaEE初阶】认识线程、创建线程
1. 认识线程(Thread) 1.1 概念 1) 线程是什么 一个线程就是一个 "执行流". 每个线程之间都可以按照顺序执行自己的代码. 多个线程之间 "同时" 执行着多份代码. 举例: 还是回到我们之前的银⾏的例⼦中。之前我们主要描…...
uniapp中uni.navigateBack返回后刷新页面数据
文章目录 一、前言1.1、[uni.navigateBack](https://uniapp.dcloud.net.cn/api/router.html#navigateback) 二、方法2.1、父页面设置钩子函数onBackPress2.2、uni.$emit和uni.$on监听通知数据变更2.2.1、子页面2.2.2、父页面 2.3、onShow钩子函数处理数据2.3.1、子页面2.3.2、父…...
sed文本 免交互
目录 什么是sed 概念 格式 基本用法 命令的选项 打印第三行 打印日志文件 打印奇数行 打印偶数行 第三行退出 删除第三行 sed在不打开文件的情况下修改文件内容 在后面添加 选项a 在字符中间添加 \n 实现追加换行 全部追加 在前面插入 选项i 替换 选项c …...
轻巧高效的剃须好工具,DOCO黑刃电动剃须刀上手
剃须刀大家都用过,我比较喜欢电动剃须刀,尤其是多刀头的悬浮剃须刀,感觉用起来很方便,剃须效率也很高。最近我在用一款DOCO小蔻的黑刃电动剃须刀,这款剃须刀轻巧易用,而且性价比超高。 相比于同类产品&…...
第15关 K8s HPA:自动水平伸缩Pod,实现弹性扩展和资源优化
------> 课程视频同步分享在今日头条和B站 大家好,我是博哥爱运维,这节课带来k8s的HPA 自动水平伸缩pod( 视频后面有彩蛋 : ) )。 我们知道,初始Pod的数量是可以设置的,同时业务也分流量高峰和低峰&a…...
接口测试工具(Jmeter)必学技巧
安装 使用JMeter的前提需要安装JDK,需要JDK1.7以上版本 目前在用的是JMeter5.2版本,大家可自行下载解压使用 运行 进入解压路径如E: \apache-jmeter-5.2\bin,双击jmeter.bat启动运行 启动后默认为英文版本,可通过Options – Choos…...
C++面试,说明const和#define的特点和区别
#define只是用来做文本替换的,例如: #define PI 3.1415926 float angel; angel 30 * PI /180; 当程序进行编译的时候。编译器会首先将"#define PI 3.1415926"以后所有代码中的PI都替换成3.1415926,然后进行编译。它的生命周期止…...
aikit 2023 3D与机械臂结合!
引言 今天我们主要了解3D摄像头是如何跟机械臂应用相结合的。我们最近准备推出一款新的机械臂套装AI Kit 2023 3D,熟悉我们的老用户应该知道,我们之前的AI Kit 2023套装使用的是2D摄像头。 随着技术进步,市场需求和领域的扩大,2D的…...
模拟退火算法应用——求解TSP问题
仅作自己学习使用 一、问题 旅行商问题(TSP) 是要求从一个城市出发,依次访问研究区所有的城市,并且只访问一次不能走回头路,最后回到起点,求一个使得总的周游路径最短的城市访问顺序。 采用模拟退火算法求解TSP问题&#x…...
【LeetCode】每日一题 2023_11_28 设计前中后队列(数组/链表/双端队列)
文章目录 刷题前唠嗑题目:设计前中后队列题目描述代码与解题思路偷看大佬题解 结语 刷题前唠嗑 LeetCode?启动!!! 这道题的难度,才是我想象中的中等题的难度好吧,昨天那玩意对我来说还是太难了…...
python基于YOLOv8全系列模型【n/s/m/l/x】开发构建不同参数量级的钢铁产业产品智能自动化检测识别系统
在前文的项目开发实践中,我们已经以钢铁产业产品缺陷检测数据场景为基准,陆续开发构建了多款目标检测模型,感兴趣的话可以自行阅读即可。 《YOLOv3老矣尚能战否?基于YOLOv3开发构建建钢铁产业产品智能自动化检测识别系统…...
力扣142. 环形链表 II
文章目录 力扣142. 环形链表 II示例代码实现总结收获 力扣142. 环形链表 II 给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,…...
【设计模式-2.2】创建型——简单工厂和工厂模式
说明:本文介绍设计模式中,创建型设计模式中的工厂模式; 飞机大战 创建型设计模式,关注于对象的创建,本文介绍的简单工厂和工厂模式同样也是。举一个游戏例子,如飞机大战游戏中,屏幕中敌人类型…...
将文件读入C中的字符数组
当您使用 C 编程语言时,您可能会遇到一些需要将文件读入字符数组的问题,例如分析每个字符的频率,或者将所有句子的每个起始词从小写转换为大写,反之亦然。该解决方案非常简单,但对于不太了解文件读取或写入的人来说可能…...
不小心删除了短信,如何在 Android 上恢复已删除的短信
不小心删除了文字消息在 Android 手机上使用可能会是一种令人痛苦的体验。这些消息可能包含有价值的信息、珍贵的回忆或重要的细节。幸运的是,您可以探索多种方法来恢复这些丢失的消息。在本文中,我们将深入研究可用于检索已删除短信的选项,并…...
Java电子招投标采购系统源码-适合于招标代理、政府采购、企业采购、等业务的企业
项目说明 随着公司的快速发展,企业人员和经营规模不断壮大,公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境,最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范,以及审…...
springBoot的实现原理;SpringBoot是什么;使用SpringBoot的核心功能;springBoot核心注解以及核心配置文件
文章目录 springBootspringBoot的实现原理什么是 Spring Boot?SpringBoot是什么为什么要使用springBootSpring Boot的核心功能Spring Boot 主要有如下优点: SpringBoot启动过程-流程Spring Boot 的核心注解是哪个?什么是 JavaConfigÿ…...
logback-spring.xml详解
《springboot使用logback日志框架超详细教程》文中,filter中最重要的两个过滤器LevelFilter(日志级别精确匹配)、ThresholdFilter(阈值过滤) 的描述非常准确: springboot使用logback日志框架超详细教程_sp…...
【Python】nn.BCEWithLogitsLoss函数详解
nn.BCEWithLogitsLoss() 是 PyTorch 中一个用于二元分类问题的损失函数,它结合了 Sigmoid 层(将输出映射到 [0,1] 范围内)和 Binary Cross Entropy(BCE)损失。这可以避免在正向和反向传播过程中可能出现梯度爆炸或梯度…...
【C++】日期类的实现
在上篇博客中我们已经学习了C中的运算符重载,我们说,操作符只能对于内置类型进行操作,对自定义类型我们需要自己定义函数去实现一系列的操作 那么这篇博客我们就专门把日期这个类单独拿出来写一下它都有哪些有意义的可以重载的运算符…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...
