多线程(1)

news/2024/5/20 21:21:13/文章来源:https://blog.csdn.net/mu_tong_/article/details/128049369

多线程


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

或者说为啥要有进程呢?

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


了解 :

这里 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.luyixian.cn/news_show_37189.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3天3定制大屏,反向PUA

摘要 本次分享一段无讨价还价余地的单人3天定制化大屏全过程(强调说拖拽屏的请绕道,和你想的不一样),要动效、要地图、要流光。天坑的心理博弈到最终解决的过程及技术思路。 前因 没啥征兆突然接到说,要在下周完成2个大屏的定制开发,起初没提…

身份安全风险分析

摘要 从勒索软件到 APT,身份风险是重要的攻击向量。 管理 Active Directory 的复杂性,导致所有组织都存在1/6的可利用的特权身份风险。 这些身份风险包括使用过时密码的本地管理员、具有不必要权限的错误配置用户、在终端上暴露的缓存凭据等。 当攻击者…

arthas进阶版排查问题之idea插件工具操作

arthas前面的文章讲了怎么去使用命令排查线上问题,线上出了问题就需要我们去排查问题和处理程序异常,但是线上一般出问题不太好解决,总有一些奇怪的问题,当然很多场景是测试测试不到的,我们不能百分百保证线上不出问题…

阿里大咖纯手写的微服务入门笔记,从基础到进阶直接封神

前言 学习是一种基础性的能力。然而,“吾生也有涯,而知也无涯。”,如果学习不注意方法,则会“以有涯随无涯,殆矣”。 学习就像吃饭睡觉一样,是人的一种本能,人人都有学习的能力。我们在刚出生的…

AcWing 搜素与图论

搜索 DFS 全排列 代码 #include<iostream> using namespace std;int vis[10], a[10];void dfs(int step, int n) {if (step n 1){for (int i 1; i < n; i)printf("%d ", a[i]);printf("\n");return;}for (int i 1; i < n; i){if (!vis[i…

【调优】大数据常见 Join 的使用场景

【调优】大数据常见 Join 的使用场景 上次写了大表和大表 join 的调优方法&#xff0c;今天总结一下大数据常见的 Join 方法。 1.Shuffle Join 大数据采用的是分布式存储&#xff0c;一个表的数据会分散在各个节点。为了进行 join&#xff0c;通常都会进行 shuffle 操作&…

ESG,TO B长期主义里的「新战役」

中国企业最好的方式是从初始阶段就植入ESG基因&#xff0c;使它逐渐从隐形变成显性基因。长期坚持此类发展导向&#xff0c;对后续发展ESG战略&#xff0c;提升ESG合规能力也将成为一种积累和准备。 作者|三七 编辑|皮爷 出品|产业家 20世纪初期&#xff0c;伦敦得到一个延…

HttpMessageConverter 消息转换器

HttpMessageConverter 简介 HttpMessageConverter 是SpringMVC中提供的一个策略接口&#xff0c;它是一个消息转换器类&#xff0c;Spring Mvc中就是由HttpMessageConverter负责转换HTTP的请求和响应。 默认情况下&#xff0c;Spring Boot 会自动加载如下消息类型转换器&…

数字验证学习笔记——UVM学习1

一、类库地图 在SV模块中&#xff0c;验证环境整体的构建&#xff0c;是从底层模块的验证组件搭建到通信和激励生成这些元素无论是软件对象的创建、访问、修改、配置&#xff0c;还是组件之间的通信等都是通过用户自定义的方式来实现的。UVM验证方法学作为之前所有方法学的融合…

【语音去噪】谱减法+维纳滤波+卡尔曼滤波语音去噪【含Matlab源码 1881期】

⛄一、谱减法维纳滤波卡尔曼滤波语音去噪简介 1 维纳滤波算法 在传统的去噪算法中,维纳滤波因其操作简单、去噪效果好,被公认为一种经典的去噪算法。语音信号在时域的表示为: yi( t) si( t) ni( t) ,其中si( t) 、ni( t) 和yi( t) 分别是第i帧原始语音信号、噪声和被噪声污染…

java检验mp4文件完整性的一个方法:使用ffmpeg

问题引入 最近笔者在写一个多线程下载视频文件的程序&#xff0c;打算让这个程序在我的空闲服务器上运行&#xff0c;但是几轮测试之后发现&#xff0c;有时候会存在下载的视频文件不完整的情况&#xff0c;这就导致了有些文件无法正常播放 问题排查 经过一周的排查后&#…

面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了

由于现在大多计算机都是多核CPU&#xff0c;多线程往往会比单线程更快&#xff0c;更能够提高并发&#xff0c;但提高并发并不意味着启动更多的线程来执行。更多的线程意味着线程创建销毁开销加大、上下文非常频繁&#xff0c;你的程序反而不能支持更高的TPS。 时间片 多任务…

Java23种设计模式之第三弹-工厂模式

说起工厂&#xff0c;我们第一反应是制作什么东西的吧~。在现实生活中&#xff0c;工厂 &#xff0c; 就是用于生成一些特定事物的厂商。 回到我们此处说的工厂模式上&#xff0c;什么是工厂模式呢 &#xff0c; 顾名思义&#xff0c;就是生成我们的对象的类就会称成为工厂。 …

关于BigInteger和BigDecimal

BigInteger BigInteger类是用于解决整形类型(含基本数据类型及对应的包装类,)无法表示特别大的数字及运算的问题,即使是占用字节数最多的整形long,能表示的范围也是有限的.理论上,你可以使用BigInteger表示任意整数基于java8中BigInteger的构造方法. BigDecimal的构造方法2 …

[附源码]计算机毕业设计JAVA汽车租赁系统

[附源码]计算机毕业设计JAVA汽车租赁系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis M…

傻白入门芯片设计,芯片键合(Die Bonding)(四)

一、键合( Bonding) 作为半导体制造的后工序&#xff0c;封装工艺包含背面研磨(Back Grinding)、划片(Dicing)、芯片键合(Die Bonding)、引线键合(Wire Bonding)及成型(Molding)等步骤。这些工艺的顺序可根据封装技术的变化进行调整、相互结合或合并。芯片键合(die bonding)工…

Linux之分区【详细总结】

目录分区介绍分区查看指令lsblk ![请添加图片描述](https://img-blog.csdnimg.cn/d7ea5468d719433ea6ee4ab0eb145770.png)lsblk -f挂载案例分五部分组成 虚拟机添加硬盘 分区 格式化 挂载 设置自动挂载虚拟机增加硬盘查看整个系统磁盘情况查询查看整个目录磁盘占用情况磁盘情况…

cpu设计和实现(协处理器hi和lo)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 很多同学可能不了解mips处理器&#xff0c;如果个人想补充一点mips cpu的知识&#xff0c;可以找些书籍资料来读一下&#xff0c;比如《See Mips R…

Gradle学习笔记之第一个Gradle项目

文章目录前言创建gradle项目gradle目录结构gradle常用命令修改maven仓库地址启用init.gradle的方法关于gradle仓库gradle包装器前言 Gradle是Android构建的基本工具&#xff0c;因此作为Android研发&#xff0c;有必要系统地学一学Gradle&#xff0c;环境windows就可以。 创建…

微服务介绍微服务环境搭建

一、微服务介绍 从互联网早起到现在&#xff0c;系统架构大体经历了下面几个过程: 单体应用架构--->垂直应用架构--->分布 式架构--->SOA架构--->微服务架构&#xff0c;当然还有悄然兴起的Service Mesh(服务网格化)。 微服务架构 微服务架构在某种程度上是面向…