当前位置: 首页 > news >正文

【后端开发】JavaEE初阶—线程的理解和编程实现

前言:

🌟🌟本期讲解多线程的知识哟~~~,希望能帮到屏幕前的你。

🌈上期博客在这里:【后端开发】JavaEE初阶——计算机是如何工作的???-CSDN博客

🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客

​ 

目录

📚️1.引言

📚️2.线程内部原理

2.1线程的作用

2.2线程和进程的区别

2.3线程的缺点

1.引入更多的线程

2.线程冲突

2.4 线程原理总结

📚️3.多线程编程

3.1代码实现

3.2两个线程执行

3.3线程创建的方式

1.直接继承Thread并重写run方法

2.实现Runnable接口,重写run方法

3.继承Thread,重写run并使用匿名内部类 

4.实现Runnable,重写run,使用匿名内部类

 5.lambda表达式

📚️5.总结 

 

📚️1.引言

Hello!!!家人们,小编又回来啦,上期讲解了关于计算机中的重要知识进程,后面我们讲到进程在频繁的申请和销毁时会造成系统开销很大,那么就要引入线程了,接下来本期就开始讲解关于后端开发的重要知识“多线程”,开始发车了。加油加油~~~🥳🥳🥳

且听小编讲解,包你学会!!!

📚️2.线程内部原理

2.1线程的作用

线程:线程可以看做是轻量化的进程

注意:线程既可以保持独立调度执行,满足了并发执行,并且省去了分配资源和释放资源带来的额外开销;这就使得线程较好的弥补了进程的缺点

2.2线程和进程的区别

前面用PCB来描述进程,本期用PCB来表示线程

操作系统对于“多任务的调度”本质上是对PCB进程的调度,而线程和进程属性基本一致,都有状态,优先级,记账信息等;

这两者的较大区别:

 此时可以看到:每个进程要申请一个内存空间,而每一个线程都共用一个内存空间;

注意:此时就有一个重要的属性概念:内存指针,多线程的PCB的内存指针指向的是同一个内存地址;那么在第一个线程申请了分配资过后,后面创建的线程就不用分配资源了,直接省去了资源分配带来的系统消耗;

当然除了内存空间,还有文件描述附表(操作硬盘)这也是多线程共用的;

 注意:能够把资源共享的线程,分成一组叫做线程组,线程组是进程的一部分

图例如下:

 注意:

在没有线程之前,进程既是分配资源的基本单位,也是调度执行的基本单位。

线程存在后,进程是资源分配的基本单位,线程是调度执行的基本单位;

对于两者有啥意义上的区别:

假如有一个桌子上有40个水果,要把这个水果吃完,就有两种方式

第一种:

第二种:

此时就能够发现:第一种模拟的是进程的方法,第二种模拟的是线程的方法,那么就可以发现:进程种方法在并发执行任务时会不断分配资源(就是房间),线程执行的时候,就公用一个内存空间(公用一个房间),就不会重新分配新资源;

2.3线程的缺点

1.引入更多的线程

图例如下:

此时可以看到,当我们引入更多的线程之后,他们之间会相互竞争,会造成拥挤(即抢占去吃苹果),这就会造成,引入过多的线程后,执行效率反而不高了;

结果:当线程有太多的时候,线程会相互竞争CPU资源了(CPU的核心数量是有限的),这样不但不会提升执行效率,还会增加系统调度的开销;

2.线程冲突

 

假如线程1和线程二看上了同一块苹果,那么就会存在两个线程之间的冲突,苹果是谁的,也不确定

结果:线程冲突,会导致代码出现逻辑上的错误即线程安全问题; 

2.4 线程原理总结

1.每个线程都是一个独立的执行流,都可以单独参与到CPU的调度中去

2.每个进程都有自己的资源,进程中的线程共用这一份资源

3.进程和进程之间不会相互影响,但是进程中的线程之间,当一个线程抛出异常,其他线程会受到影响,导致整个线程异常终止

4同一个进程内的线程之间会相互影响,导致线程安全问题

5.线程不是越多越好,太多了会导致系统调度开销增大

📚️3.多线程编程

在写代码的时候可以用多进程编程,和多线程编程;但是在Java库中对应的多进程编程是没有对应API的,并且前面也讲到多线程编程在资源分配上优于多进程编程,所以就用多线程编程;

3.1代码实现

首先创建一个类去继承Thread类,这里的Thread类是由java.lang包里的,并要重写run()方法,这个方法即是线程的入口,代码如下:

class mythread extends Thread{//重写run方法,该线程的入口方法@Overridepublic void run(){}}
}

注意事项;

1.这里的方法不用调用,当线程创建好之后,JVM会自动帮我们进行调用;

2.每个进程中都有至少有一个线程,进程中的第一个线程就是主线程,所以我们的main方法就是主线程的入口;

3.Thread类本身会有一个run()方法

创建实例,调用start方法,这样才会在系统内核中创建真正的线程,代码如下:

public static void main(String[] args) {//构造实例mythread t=new mythread();//Thread t=new Thread();t.start();

 注意:

在实例化对象时,可以直接用mythread类,但是也可以使用Thread来接收,这就是完成了一个向上转型

这里如果直接new一个Thread的对象后,创建线程后,JVM会自动调用这个类本身的run() 

我们这里简单演示一下,线程的代码和结果;

public class threadDeom1 {public static void main(String[] args) {//构造实例//mythread t=new mythread();Thread t=new mythread();t.start();}
}
//写一个类继承thrad
class mythread extends Thread{//重写run方法,该线程的入口方法@Overridepublic void run(){while (true){System.out.println("这是thread线程");//防止打印过快try {Thread.sleep(1000);}catch (InterruptedException e){throw new RuntimeException();//抛出异常}}}
}

 注意:这里的Sleep是线程休眠的意思,1000代表的ms所以这里就是一秒,并且此时还要抛出异常,程序才能正常执行;

结果:

此时这张图只是一部分,线程会不断执行,这里小编只是截取了一小部分;

3.2两个线程执行

这里的代码如下:

public static void main(String[] args) {//构造实例//mythread t=new mythread();Thread t=new mythread();t.start();while (true){System.out.println("这是main线程");try {Thread.sleep(1000);}catch (InterruptedException e){throw new RuntimeException();}}}
}
//写一个类继承thrad
class mythread extends Thread{//重写run方法,该线程的入口方法@Overridepublic void run(){while (true){System.out.println("这是thread线程");//防止打印过快try {Thread.sleep(1000);}catch (InterruptedException e){throw new RuntimeException();//抛出异常}}}

这里存在两个死循环,但是这两个输出语句仍然进行了打印,这是为什么呢???

注意:线程是两个独立的执行流,所以此处在调用start方法后,线程就有两种,一路继续沿着main方法、即主线程,另一种就是进入线程的run方法;

这里的打印顺序是不确定的,因为操作系统内核中有“调度器这一模块”,即一种随机调度的效果

随机调度

一个线程什么时候被CPU调度是不确定的;

一个线程什么时候从CPU上下来,给其他线程让位也是不确定的;

上述代码输出结果:

这里大家可以多次实验一下,可以看到为第一个输出的总是main线程呢???

注意:当使用start方法后,主线程会直接向下执行,但是操作系统的内核就要根据刚才的api创建出线程来,并执行run方法;又因为创建线程也有开销(比进程小很多,但是不为0),所以会导致主线程更快;

3.3线程创建的方式

1.直接继承Thread并重写run方法

代码实现:

 public static void main(String[] args) {//构造实例//mythread t=new mythread();Thread t=new mythread();t.start();}
}
//写一个类继承thrad
class mythread extends Thread{//重写run方法,该线程的入口方法@Overridepublic void run(){}

注意:重写为了特定功能的展开,满足我们特定的需求;

2.实现Runnable接口,重写run方法

 代码实现如下:

public static void main(String[] args) {Runnable runnable=new mythread1();//实现runnable接口Thread t=new Thread(runnable);t.start();//第二种写法Thread t=new Thread(new mythread1());t.start();//第三种写法mythread1 mythread1=new mythread1();Thread t=new Thread(mythread1);t.start();}
}
class mythread1 implements Runnable{@Overridepublic void run() {}

注意:小编这里介绍了三种写法,其实本质都是一样的,都是输入线程对象,并且在实例化对象时,一个使用了向上转型,还有一个没有使用向上转型而已;

3.继承Thread,重写run并使用匿名内部类 

代码实现如下:

public static void main(String[] args) {Thread t=new Thread(){public void run(){}};t.start();while (true){System.out.println("这是main线程");try {Thread.sleep(1000);}catch (InterruptedException e){throw new RuntimeException();}}}

注意:{}中可以主要是定义子类的属性和方法,这里最主要还是进行run方法的重写;下面的死循环是为了方便进行两种线程存在执行情况;

4.实现Runnable,重写run,使用匿名内部类

代码实现如下:

 public static void main(String[] args) {Thread t=new Thread(new Runnable() {@Overridepublic void run() {}});t.start();while (true){System.out.println("这是mian线程");try {Thread.sleep(1000);}catch (InterruptedException e){throw new RuntimeException();}}}

注意:这里Thread括号里使用的参数,是Runnable接口匿名内部类的实例,这个就像是之前在lambda表达式中,比较器的写法,这里大家可以参考一下:【数据结构】Lambda表达式的应用-CSDN博客

 5.lambda表达式

代码如下:

public static void main(String[] args) {//lambda表达式Thread t=new Thread(()->{while (true){}});t.start();while (true){System.out.println("这是main线程");try {Thread.sleep(1000);}catch (InterruptedException e){throw new RuntimeException();}}}

注意:这里的lambda表达式中()内没有参数,其实这就是实现Runnable接口匿名内部类的一种写法,由于只有一个方法,且参数为空,所以这个括号内也就是空,并且后面的方法体就是重写run()方法;

📚️5.总结 

💬💬小编本期讲解了JavaEE初阶的重要知识线程,以及和进程之间的关系,以及线程的优点和缺点,并且讲述了在编程中线程的实现,以及线程的创建的五种方式,还附上了代码供小伙伴参考参考~~~

代码上传Gitee啦:朱海洋/Thread (gitee.com)

🌅🌅🌅~~~~最后希望与诸君共勉,共同进步!!!


💪💪💪以上就是本期内容了, 感兴趣的话,就关注小编吧。

                                                                 😊😊  期待你的关注~~~

相关文章:

【后端开发】JavaEE初阶—线程的理解和编程实现

前言: 🌟🌟本期讲解多线程的知识哟~~~,希望能帮到屏幕前的你。 🌈上期博客在这里:【后端开发】JavaEE初阶——计算机是如何工作的???-CSDN博客 🌈感兴趣的小伙…...

Matlab simulink建模与仿真 第十九章(生成C代码)

一、Configuration Parameters模型参数配置 1、仿真时间 (1)在Solver选项卡中可以设置仿真的起始时间和结束时间,一般起始时间设为0,而结束时间按需设置。 (2)如果希望仿真不会自动暂停(也就…...

遍历9个格子winmine!StepBlock和遍历8个格子winmine!StepBox的对决

遍历9个格子winmine!StepBlock和遍历8个格子winmine!StepBox的对决 第一部分:windbg调试记录。 0: kd> g Breakpoint 10 hit winmine!DoButton1Up: 001b:0100390e a130510001 mov eax,dword ptr [winmine!xCur (01005130)] 0: kd> kc # 00 winmine…...

Python中的文件编码:揭开字符世界的神秘面纱

引言 在计算机系统中,数据是以二进制形式存储的。而我们日常见到的文字、符号等信息,则需要通过特定的方式转化为二进制数据,这就是编码的过程。不同的编码方式决定了如何将字符映射成字节序列。选择合适的编码方案不仅能够保证信息传输的准…...

Vue3使用hiprint——批次打印条码

例图:打印编号 一、安装Vue-Plugin-HiPrint 要开始使用 Vue-Plugin-HiPrint,首先需要安装它。可以使用 npm npm install vue-plugin-hiprint --save 二、在main.js中引入 Vue-Plugin-HiPrint 在您的 main.js 或任何其他入口文件中,您可以按…...

智慧城市主要运营模式分析

(一)运营模式演变 作为新一代信息化技术落地应用的新事物,智慧城市在建设模式方面借鉴了大量工程建设的经验,如平行发包(DBB,Design-Bid-Build)、EPC工程总承包、PPP等模式等,这些模式在不同的发展阶段和条件下发挥了重要作用。 在智慧城市发展模式从政府主导、以建为主、…...

典型的MVC设计模式:使用JSP和JavaBean相结合的方式来动态生成网页内容典型的MVC设计模式

先看代码与实现&#xff1a; 文件结构 triangle_area4.jsp <% page contentType"text/html;charsetUTF-8" pageEncoding"UTF-8" %> <html> <body> <%--<jsp:useBean>&#xff1a;用于在JSP中实例化JavaBean。在这里&#xff0c…...

Vue引入js脚本问题记录(附解决办法)

目录 一、需求 二、import引入问题记录 三、解决方式 一、需求 我想在我的Vue项目中引入jquery.js和bootstrap.js这种脚本文件&#xff0c;但发现不能单纯的import引入&#xff0c;问题如下。 二、import引入问题记录 我直接这么引入&#xff0c;发现控制台报错TypeError: …...

数据清洗与数据治理的关系

数据清洗与数据治理是数据处理过程中的两个重要步骤&#xff0c;它们共同确保数据的质量和可靠性&#xff0c;以便于数据分析和决策支持。 数据清洗 数据清洗&#xff08;Data Cleaning&#xff09;是指识别并纠正或删除数据集中的不准确、不完整、重复或错误的记录的过程。数…...

树莓派pico上手

0 介绍 不同于作为单板计算机的树莓派5&#xff0c;树莓派 pico 是一款低成本、高性能的微控制器板&#xff0c;具有灵活的数字接口。主要功能包括&#xff1a; 英国树莓派公司设计的 RP2040 微控制器芯片双核 Arm Cortex M0 处理器&#xff0c;弹性的时钟频率高达 133 MHz26…...

TypeError: load() missing 1 required positional argument: ‘Loader‘

标题TypeError: load() missing 1 required positional argument: ‘Loader’ 源码&#xff1a; 处理后&#xff1a; 顺利通过&#xff0c;由于yaml版本导致的问题...

根据软件架构设计与评估的叙述开发一套机器学习应用开发平台

案例 阅读以下关于软件架构设计与评估的叙述&#xff0c;回答问题 1和问题 2。 【说明】 某公司拟开发一套机器学习应用开发平台&#xff0c;支持用户使用浏览器在线进行基于机器学习的智能应用开发活动。该平台的核心应用场景是用户通过拖拽算法组件灵活定义机器学习流程&…...

【隐私计算篇】利用多方安全计算MPC实现VGG16人脸识别隐私推理

1. 背景介绍 本文主要介绍一种利用多方安全计算MPC技术&#xff0c;实现VGG16的人脸识别模型&#xff0c;侧重于模型推理阶段&#xff0c;目前已经公开专利&#xff0c;因此以下内容的分享都是基于公开材料。该分享涉及到最小化多方安全计算(MPC)以及明密文混合计算的思想&…...

Python 入门教程(3)基础知识 | 3.7、pass 关键字

文章目录 一、pass 关键字1、定义与用法2、pass 关键字的用法2.1、函数定义中的占位符2.2、 类定义中的占位符2.3、条件语句中的占位符2.4、循环中的占位符 3、注意事项 一、pass 关键字 1、定义与用法 pass语句用作将来代码的占位符。当执行pass语句时&#xff0c;不会有任何…...

nodejs基于vue+express度假村旅游管理系统设计与实现7t82p

目录 功能介绍数据库设计具体实现截图技术栈技术论证解决的思路论文目录核心代码风格详细视频演示源码获取 功能介绍 实现了一个完整的农家乐系统&#xff0c;其中主要有用户表模块、关于我们模块、收藏表模块、公告信息模块、酒店预订模块、酒店信息模块、景区信息模块、景区…...

【裸机装机系列】16.kali(ubuntu)-安装linux和win双系统-重装win11步骤

推荐阅读&#xff1a; 1.kali(ubuntu)-为什么弃用ubuntu&#xff0c;而选择基于debian的kali操作系统 注意&#xff1a; 要先装windows&#xff0c;再装linux&#xff0c;不然linux的启动分区会被覆盖掉。为什么双系统要先装windows呢&#xff1f; 在一个新硬盘上&#xff0…...

基于TypeScript+React+AntDesign 的车辆车型管理页面

项目目录结构&#xff1a; my-app/├── node_modules/├── public/├── src/│ ├── App.js│ ├── VehicleForm.js│ └── index.js├── package.json└── README.md目录 1.创建项目 2.列表页面VehicleForm.js,预留接口使用axios 1.创建项目 npx crea…...

sentinel-dashboard数据 redis 持久化

概述 随着微服务的流行&#xff0c;服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件&#xff0c;主要以流量为切入点&#xff0c;从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来…...

【C++】——vector深度剖析模拟实现

低头赶路&#xff0c;敬事如仪 目录 1、模拟vector 1.1底层结构 1.2构造析构 1.3尾插扩容 1.4迭代器 1.5增删查改 1.6模拟中的注意事项 2、vector模拟补充 2.1迭代器区间构造问题 2.2memcpy深浅拷贝问题 2.3动态二维数组的模拟及遍历 1、模拟vector 想要模拟实现自…...

OpenCV特征检测(11)从一组点中检测直线的函数

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在一组点中使用标准霍夫变换查找直线。 该函数使用霍夫变换的一种改进方法在一组点中查找直线。 HoughLinesPointSet 是 OpenCV 中的一个函数&a…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

centos 7 部署awstats 网站访问检测

一、基础环境准备&#xff08;两种安装方式都要做&#xff09; bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats&#xff0…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...

Web后端基础(基础知识)

BS架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器架构模式。客户端只需要浏览器&#xff0c;应用程序的逻辑和数据都存储在服务端。 优点&#xff1a;维护方便缺点&#xff1a;体验一般 CS架构&#xff1a;Client/Server&#xff0c;客户端/服务器架构模式。需要单独…...

深入理解Optional:处理空指针异常

1. 使用Optional处理可能为空的集合 在Java开发中&#xff0c;集合判空是一个常见但容易出错的场景。传统方式虽然可行&#xff0c;但存在一些潜在问题&#xff1a; // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...

实战三:开发网页端界面完成黑白视频转为彩色视频

​一、需求描述 设计一个简单的视频上色应用&#xff0c;用户可以通过网页界面上传黑白视频&#xff0c;系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观&#xff0c;不需要了解技术细节。 效果图 ​二、实现思路 总体思路&#xff1a; 用户通过Gradio界面上…...

go 里面的指针

指针 在 Go 中&#xff0c;指针&#xff08;pointer&#xff09;是一个变量的内存地址&#xff0c;就像 C 语言那样&#xff1a; a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10&#xff0c;通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...

rm视觉学习1-自瞄部分

首先先感谢中南大学的开源&#xff0c;提供了很全面的思路&#xff0c;减少了很多基础性的开发研究 我看的阅读的是中南大学FYT战队开源视觉代码 链接&#xff1a;https://github.com/CSU-FYT-Vision/FYT2024_vision.git 1.框架&#xff1a; 代码框架结构&#xff1a;readme有…...