多线程(1)

news2025/7/17 10:54:41

多线程


前言 : 上文主要了解到了进程, 那么为啥需要引入进程呢?

或者说为啥要有进程呢?

其实这里最主要的目的是为了解决 并发编程 这样的问题。


了解 :

这里 cpu 进入了多核心的时代,想要进一步提高程序的执行速度,就需要充分的利用CPU的多核心资源.

cpu 再往小了做 就比较困难 ,此时先要通过单核心提高执行速度就遇到了困难, 既然一个核心不行那么多个核心呢?显然是可以的,要不一句话人多力量大吗。

但是不是说 cpu的核心多了,程序一下就跑的快了 , 还需要你的程序代码能供把这些 cpu 核心给用上 。

引出多线程

回到进程这里, 关于我们的多进程编程,已经是可以解决并发编程的问题(可以利用cpu的多核资源) ,但是因为频繁的创建和销毁进程对cpu的资源开销是比较大的 ,调度进程同样也是 ,另外 使用进程会拖慢了我们的运行速度 。

正因为 使用 进程, 开销比较大速度比较慢 , 为了解决这个问题 我们的线程就应运而生 , 这里线程也称为 轻量级进程

那么为啥说进程的开销比较大呢 ?

这里主要就在 资源分配 / 回收上 , 之前说过 进程是分配资源的最小单位, 我们想要创建进程就需要给它分配资源 (内存 资源, 硬盘资源等), 此时分配资源就需要花费一定的时间.


那么为啥说线程的开销小速度快呢 ?

其实也就是线程将分配资源/ 回收上 / (申请资源和回收资源) 的这一部分开销给省略了


为了好理解 这里举一个例子 :


那种流水线的厂子不知道大家进没进过,鄙人有幸在里面做过一天的事情,可以说是非常的累的 。

假设 有一个厂是生产手机的 ,一天能生产 1w部手机, 但是市场上需求量非常高,导致一天生产 1w部手机可能不够,此时厂长就想要提高产量,让每天有 2 w 部手机产出 ,这里就有两种解决方式

1.在其他的方法重新开一个厂,然后两个厂同时生产那么此时是不是一天就可以达到2w部,


2.既然是流水线,那么是不是可以多增加一条流水线,此时是不是同样可以使 一天 2 w 部 (假设之前的厂只有一条流水线,但大部分不可能是这个情况)


对比一下这两种方法,你觉的是 1 好 还是 2 好 显然是 2 好,因为重新开一个厂 ,需要挑选场地, 建厂, 装流水线等都是需要花钱的,而 方法二 新增一条流水线 ,就不需要花额外的钱去 挑选场地,建厂 。


这里是不是就可以认为 方法一 是使用进程 ,而方法二是线程呢?


既然将 进程 想象成 厂 , 线程想象程流水线, 这里就可以得出 进程和线程之前的关系 。

一个进程可以包含一个线程,也可以包含多个线程(注意 : 这里不能没有)

好理解 : 厂吗不可能没有流水线的,要不然咋生产商品赚钱呢 做慈善吗, 同样厂可以拥有一条,也可以拥有多条.

另外 : 正因为我们的厂内可以拥有多条流水线,那么是不是只有创建厂和新建第一条流水线的时候开销比较大,之后添加的流水线,就直接在厂内添加即可? 放到进程和线程上来说,也就是我们启动第一个线程的时候开销是比较大的 去申请 资源 (没有资源咋办事吗),后面的进程就省事了, 因为是不会去申请和释放资源的直接用即可.


通过上面总结 : 一个进程可以包含多个线程程 , 这些线程之间共用同一份资源


补充: 这里的资源指的是 内存 和 文件描述符表

内存 : 线程1 new 的对象 在 线程 2 , 3 , 4 等 都可以直接使用

文件描述符表 : 线程 1 打开的文件 , 在线程 2 , 3 , 4 等轴都可以直接使用 .

另外 : 操作实际调度的时候,是以线程为单位进行调度的.

回到上文 所说的进程调度就不够准确, 准确来说是以线程为单位来调度的 , 如果站在上文的角度 说进程调度就相当于每一个进程只有一个线程的情况.

补充 : 这里每个进程中的线程都是独立在CPU上进行调度的 , 换句话来说 线程是操作系统调度执行的基本单位


另外 : 一个线程就是一个 “执行流”. 每个线程之间都可以按照顺序执行自己的代码. 多个线程之间 “同时” 执行着多份代码.


看完上面, 有没有想过 我们要如何去描述一个线程呢 ?

在上文 介绍的 进程 通过 PCB来描述的,那么线程是不是也是呢 ?

答案 : 我们的线程同样也是通过PCB来描述的.


注意 :这里别晕,其实PCB对应的就是线程,为啥上文说是对应进程呢?因为是站在一个进程只有一个线程的角度,所以才会所对应PCB


此时就有趣了,既然 线程也是通过 PCB 来描述的,那么进程又可以拥有多个线程,那么进程是不是会对应多个PCB呢 ?


没错 我们的进程 是可能对应多个PCB的,正因线程同样也是被PCB描述的,进程中又含有多个线程所以,进程就会对应多个PCB了,

在这里插入图片描述

那么还有一个问题 :上文介绍 过一对PCB的属性 状态 ,上下文, 优先级 ,记账信息,是每个线程都有自己的还是共同使用一个呢 ?

答案 : 每个线程都会拥有自己的各自记录各自的,但是 同一进程里的PCB之间, pid是一样的,内存指针和文件描述符表也是一样的.


描述 看完 , 再来像一个问题 , 既然我们使用多线程 比较轻量 ,能提高效率,那么我们能不能一直创建线程呢?

答案 不是的 ,虽然创建线程是能提高我们的 效率但是,线程一多,会导致多个线程进行资源的竞争,此时肯定会有没有抢到的线程(资源就那么一份),此时就会拖慢我们的效率。


举例 :

图一 :

在这里插入图片描述


图二 :

在这里插入图片描述


总结 : 进程和线程的区别 (注意 :这里是非常经典的面试题)


1.进程是包含线程的 , 一个进程可以含有多个线程

2.进程和线程都是处理并发编程的场景

3.进程比较重 , 线程比较轻 .

为啥进程重 因为频繁的创建和消费进程的时候效率是非常低的(消耗的资源多) , 而线程就不会 ,

为啥线程轻 因为只有启动第一个线程的时候才会去创建和消耗资源,其他的线程是公用同一份资源的。

4.进程是具有独立性的, 每一个进程之间都拥有各自的虚拟地址空间,一个进程挂了,其他进程是不受影响的,而线程则不会,因为线程之间公用同一块资源,所以当其中一个线程出现了异常就可能导致整个进程崩溃

5.进程是操作系统分配资源的基本单位

6.线程是操作系统调度的执行的基本单位


理论知识 看完,下面来看我们java如何进行多线程操作

Thread

引用 :

线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对用户层提供了一些 API 供用户使用(例如 Linux 的 pthread 库).

Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进行了进一步的抽象和封装.


下面我们就通过 Thread 来创建我们的线程。


第一步 : 打开idea 创建类
在这里插入图片描述


第二步 : 创建 Thread 对象

在这里插入图片描述


问题 一: 为啥使用 Thread 类不需要通过 import 导入别的 包 ?

问题二 :你还见过那些类也是类似的.


答: 我们之前见过的String , StringBuilder ,StringBuffer 都是这样的,因为他们都是在java.lang底下的,因为java.lang 编译器会自动给我们导入.

问题 看完了, 回到上面,其实这里的线程还是没有创建完成,因为我们创建线程是希望线程成为一个独立的执行流(执行一段代码),上面的程序并没有给他执行的任务。

那么这里的问题就来到了如何指定他的任务呢 ?

这里总共有 5种方法,下面就来看看


方法一 : 实现一个类继承Thread 重写run方法 .

在这里插入图片描述


run方法里面就写我们需要执行的任务 .


下面就来执行我们的程序看一下效果 :

在这里插入图片描述


注意 : 只有我们 通过 s.start() 去启动线程的时候才会创建线程, 而 new 是不会创建线程的 .

之前说过我们的线程是用来解决并发编程的,上面并没有体现出来,下面我们来修改一下我们的代码,来看看如何并发的。

在这里插入图片描述


因为这里执行的非常快我们就可以通过 sleep 让线程休眠一秒来看执行效果

在这里插入图片描述


正因为 抢占式执行,才会出现我们的线程安全问题 . 随机调度导致多个线程竞争同一份资源.


另外在来补充一下 t.runt.start 的区别

在这里插入图片描述

扩充 : 通过 jdk 自带的工具jconsole 查看当前的 java 进程中的所有进程

在这里插入图片描述

图二 : 观察线程

在这里插入图片描述

图三 :

在这里插入图片描述

此时第一种写法就完成 了 ,下面来看第二种 :


方法二 : 实现Runnable 接口

在这里插入图片描述

这里的Runnable 作用 是描述 一个要执行的任务 , run 方法是任务的执行细节.

这里我们不能直接通过 new Runnable (接口不能实例化) 传给Thread 来执行 , 所以需要去实现一个类 MyRunnable ,然后通过 new 传给 Thread类.

可以看到, 我们的任务是 通过 Runnable 描述的,我们将任务传给了线程, 这里就 将线程 和 线程需要干的活进行了解耦合操作.

这里的好处 就是未来的莫一天,我们要改代码,不用多线程了,使用多进程, 或者线程池或者协程等 此时代码的改动就比较小.


方法三 : 匿名内部类 继承Thread 重写 run方法

知识点 : 《内部类》

在这里插入图片描述


这里的方法三其实就是方法一,只不过这里使用了匿名内部类


方法四 : 匿名内部类 实现 Runnable接口 ,重写 run方法

在这里插入图片描述


这个写法与 写法2 等价 ,只不过实现 Runnable 的是一个匿名内部内 .


方法五 : 使用Lambda 表达式

知识点 : 反射 - 枚举 - Lambda表达式

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/36890.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决:给 VSCode 手动添加(解压压缩包)相关插件的问题

1、一般的添加方式为: 在 VSCode 里面扩展程序里面直接搜索添加即可; 2、如何手动添加一个 VSCode 相关插件? 其一、首先把相关文件解压,解压成一个文件夹; 其二、找到 VSCode 的 extensions 的地址并把 A 所述文件…

【Java 设计模式】创建者模式 之抽象工厂模式

抽象工厂模式1 概念2 角色3 甜品店案例3.1 类图3.2 实现4 优缺点1 概念 产品族:华为既有华为手机,也有华为电脑、华为平板,华为手机、华为电脑和华为平板属于一个产品族,因为他们都是华为品牌。产品等级:有华为手机&am…

骁龙咣咣咣三脚,再次改写格局

鱼羊 萧箫 发自 凹非寺量子位 | 公众号 QbitAI盆友们,骁龙,已经不再是以前那个骁龙了。就在大家吃瓜安卓新旗舰芯片谁家首发之际,骁龙峰会的绝对主角,却只差没把“时代变了”写在脸上:先是第一天直接挤爆AI牙膏管&…

创建Struts2项目并实现一个例子

文章目录一、创建Struts2项目二、struts2的一个例子一、创建Struts2项目 本人所用idea应用程序。以下全部内容,都是以idea为例 创建项目前,你需要下载Struts2所需要的jar包,你可以直接去官网下载,也可以点击https://pan.baidu.c…

LeetCode - 1419 数青蛙

题目来源 1419. 数青蛙 - 力扣(LeetCode) 题目描述 给你一个字符串 croakOfFrogs,它表示不同青蛙发出的蛙鸣声(字符串 "croak" )的组合。由于同一时间可以有多只青蛙呱呱作响,所以 croakOfFrog…

基于蜻蜓优化算法的认知无线电网络的服务质量研究附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。 🍎个人主页:Matlab科研工作室 🍊个人信条:格物致知。 更多Matlab仿真内容点击👇 智能优化算法 …

葡萄糖-聚乙二醇-牛血清白蛋白,BSA-PEG-Glucose,牛血清白蛋白-PEG-葡萄糖

葡萄糖-聚乙二醇-牛血清白蛋白,BSA-PEG-Glucose,牛血清白蛋白-PEG-葡萄糖 牛血清白蛋白(BSA),是牛血清中的一种球蛋白,包含607个氨基酸残基,分子量为66.446KDa,可以提供PEG接枝修饰葡萄糖,葡萄…

独家 | 人工智能的记忆与泛化(附链接)

作者:Manuel Brenner 翻译:陈之炎校对:赵茹萱本文约4500字,建议阅读8分钟本文为你介绍了人工智能 记忆与泛化相关知识。标签:人工智能,记忆,泛化“对不可见数据的泛化能力是机器学习的核心。”当…

测试用例设计方法之场景设计法

基本流:采用直黑线表示,是经过用例的最简单的路径(无任何差错,程序从开始直接执行到结束) 备选流:采用不同颜色表示,一个备选流可能从基本流开始,在某个特定条件下执行,…

声明变量let和const

1.let的声明 JavaScript中用var关键字来声明变量,而在ES6中,还可以使用新增的let的关键字来声明变量。与var不同的是let声明的变量只能在代码块中有效。 {let a5;var b6;console.log(a);console.log(b);} 执行结果: 能正常输出,…

178:vue+openlayers 加载多种形式Esri地图

第178个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+openlayers中绘制自定义图形,利用Geojson的writeFeatures,来生成geojson格式的数据,然后使用file-saver来导出geojson。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果; 注意如果OpenStreetM…

【论文阅读】半监督时序动作检测 Semi-Supervised Action Detection

文章目录1. Learning Temporal Action Proposals With Fewer Labels (ICCV 2019)论文目的——拟解决问题贡献——创新实现流程详细方法2. Self-Supervised Learning for Semi-Supervised Temporal Action Proposal (CVPR 2021)论文目的——拟解决问题贡献——创新实现流程详细方…

微服务之间,最佳的调用方式是什么?

在微服务架构中,需要调用很多服务才能完成一项功能。服务之间如何互相调用就变成微服务架构中的一个关键问题。服务调用有两种方式,一种是RPC方式,另一种是事件驱动(Event-driven)方式,也就是发消息方式。消…

多模态实战视频生成文本:基于VGG与LSTM实现图像对应的描述文本生成任务

任务描述:本教程将帮助你利用深度学习的方法为照片等图像自动生成文字描述,图像描述是结合计算机视觉与自然语言处理的综合任务,输入一幅图像, 输出一段描述该图像的文字,这一任务要求模型可以识别图片中的物体、理解物体间的关系,并用一句自然语言表达出来。 运行环境:在…

体系结构30_同步性能问题

栅栏(barrier)同步 栅栏强制所有到达该栅栏的进程进行等待,直到全部的进程到达栅栏,然后释放全部的进程,从而形成同步。 栅栏的典型实现是要用两个旋转锁:一个用来记录到达栅栏的进程数,另一个用…

Java 对象拷贝原理剖析及最佳实践

1 前言 对象拷贝,是我们在开发过程中,绕不开的过程,既存在于 Po、Dto、Do、Vo 各个表现层数据的转换,也存在于系统交互如序列化、反序列化。 Java 对象拷贝分为深拷贝和浅拷贝,目前常用的属性拷贝工具,包…

【外卖项目实战开发二】

文章目录1、完善登录功能问题分析代码实现2、新增员工需求分析数据模型代码开发3、员工信息分页查询需求分析代码开发4、启用/禁用员工账号需求分析代码开发代码修复5、编辑员工信息需求分析代码开发1、完善登录功能 问题分析 前面我们已经完成了后台系统的员工登录功能开发&…

zabbix集群搭建分布式监控的操作步骤

作用: 分担server的集中式压力解决多机房之间的网络延迟问题环境准备: 服务器1:zabbix-server 服务器2:zabbix-proxy 服务器3:zabbix-agent 关系:zabbix-agent发送数据到代理,代理汇总数据发送…

Linux多核运行机制(SMP)

一、Linux内核兼容多处理器要求 有多个 CPU 处理器 的 系统中 , Linux 内核需要处理的问题 : 1、公平共享 : CPU 的负载 , 需要公平地共享 , 不能出现某个CPU空闲 , 造成资源浪费。 2、可设置进程 与 CPU 亲和性 : 可以为 某些类型的 进程 与 指定的 处理器设置亲和性 , 可以针…

QT:debug,打不开头文件以及qDebug和Q_CLASSINFO的使用

这个是因为链接器在给定路径上搜索不到对应的头文件,而大多数的Qt相关的头文件都集中在一个include文件夹里: 我电脑上的路径是:C:\Qt\Qt5.9.7\5.9.7\msvc2017_64\include 然后我们在项目设置里: 注意,这边要加上\*&…