第一章、并发编程的挑战 

并发和并行:指多线程或多进程 

线程的本质:操作系统能够进行运算调度的最小单位,是进程(Process)中的实际工作单元

进程的本质:操作系统进行资源分配和调度的基本单位,是程序的一次动态执行过程。

多线程定义:在同一个进程内创建多个线程,共享进程的内存和资源(如堆、全局变量),但每个线程有独立的栈和程序计数器。

多进程定义:操作系统同时运行多个独立的进程,每个进程有独立的内存空间和系统资源。

操作系统:操作系统(Operating System, OS)是计算机系统的核心管理者硬件与软件的桥梁

并发和并行的区别是什么? 

【本文专注于多线程】

并行:不同的线程或进程间互不干扰 

并发:不同的线程之间有资源的竞争

线程的基本代码: 

Thread t1---->创建第一个线程

Thread t2----->创建第二个线程

运行时是两个任务交替运行;去掉t1.join()/t2.join(),运行是3个任务交替运行

线程的几种状态: 

操作系统的指令执行不是立刻的,而是一个任务调度的过程;指令的最终执行靠的是CPU里的运算核心。

在任务管理器中,句柄指变量,核心指CPU中间的运算核心【ALU】,每个核心同一时刻只能执行一个任务。【因为电压信号同时传输会合并为一个信号】;内核为14指CPU同时只能执行14个指令,每个基础指令是线程级别,可以认为是一个任务--->CPU同时最多执行14个任务;快速切换任务执行;

1、CPU内部的信号指令1010,都是电压信号,高电压代表1,低电压代表0,电压信号不能同时传递,必须是排队传输。

2、计算机内部的各个硬件之间的指令传输都是高低电压传输硬件之间的指令传输,由于电压传输,每个指令必须得排队。 

 就绪队列:每个任务都进入就绪态,操作系统随时可以选中去执行,由就绪态选中变成运行状态,当剩的时间片不多,执行完时,从就绪队列清除,变成死亡状态。

时间片: 决定了任务在就绪队列中能连续占用CPU的时长

在电脑中任何任务的提交不能绕过操作系统。

队列为什么不是先进先出?

 先进先出是公平队列,整体性能损耗较大;

操作系统里是非公平队列,判定时是按顺序判断该任务是否符合条件,只有符合条件的才会被选中;所以态既叫队列,又叫非公平队列;

为什么微信,QQ等一直在运行状态? 

里面有一个死循环,当用户点击关闭触发条件判定, 循环判定不满足,程序结束。

在微信、QQ等应用保持后台运行的场景中,“死循环”通常指的是主线程的事件循环(Event Loop)。

用户是不能让程序直接进入运行态的,用户只能交给操作系统让它进入就绪态。

 

代码内存图:

 方法的执行都是拷贝入栈;方法本身也是一个栈结构;Java的引用类型变量底层是C语言的指针;new的两个对象在底层是C语言创建的空间,是在堆区域创建了两个线程对象;t1对象属于线程类;Thread里有很多属性和方法,上述代码只是对run方法进行重写;

run和start方法是非静态方法,在每个对象里都有一份;

main方法被static修饰,只有一份;

方法的调用都是拷贝入栈;

t1的start方法拷贝入栈后,t1的start方法会把它的整个线程信息交给操作系统的就绪队列,提交完后出栈------t2的start拷贝入栈,t2的start把t2的信息提交给操作系统,t2在就绪队列,t2再出栈-----主方法本身也在就绪队列里----操作系统随时选中执行,选中后再创建新线程【比如针对t1创建t1线程栈,针对t2创建t2线程栈】---t1被操作系统选中后会执行里面的run方法,run方法拷贝进入t1线程;

类.方法-----方法是静态的

通过主线程可以创建子线程,创建完之后就相互独立;

先进入就绪态被操作系统选中的概率大,但是顺序不确定;

上下文切换【面试点】

CPU每次执行完任务会把任务执行到哪了记下来,下次再执行时会去读从哪接着开始执行,时间损耗是毫秒级。

定义:

上下文切换是操作系统在多任务环境中,将CPU从一个线程(或进程)切换到另一个线程(或进程)时,保存当前任务状态并恢复新任务状态的过程。它是并发编程中影响性能的关键因素之一。

时间片一般是几毫秒——几十毫秒,不同操作系统不一样。

 多线程一定会涉及到上下文切换,频繁的上下文切换一定会额外消耗CPU的时间。

什么情况下用多线程?【面试题】

多线程在CPU浪费严重的情况下使用。【执行时间和等待时间比例特别悬殊的情况相爱,采用多线程】(一定是io密集,就是大量的高频访问网落访问硬盘)

IO密集型(输入输出):出现了大量的CPU等待时间。

io:访问网落,访问硬盘

指令密集型:大量给CPU任务---单线程合适

join() 是 Java 多线程编程中的一个核心方法,用于让当前线程等待另一个线程执行完毕

t1、t2都执行完时,for循环才会执行。

t1、t2的执行互不等待;只有t1执行完时,t2才会发出join指令

 在栈里,只有栈顶的元素处于运行状态,不在栈顶处于停止运行状态。

在这个代码里,对比单线程执行两个for循环和两个线程分别执行for循环哪个更快。【thread.jon和输出时间戳互换】

串行:单线程---并发:多线程

只有一个核心,单线程最快

用什么工具来监控内存?----Lmbench3可以测量上下文切换的时长,指令:vmstat---可以测量上下文切换的次数【面试点】

如何减少上下文切换?【面试点】----无锁并发编程、CAS算法、使用最少线程和使用协程。


 


grep指令:文本搜索工具;在文件或输入流中匹配指定模式(正则表达式)

awk指令:文本处理与报表生成;逐行处理文本,按字段(默认以空格/Tab分隔)提取、计算或格式化输出。

sort指令: 文本排序工具;对文件或输入流按行排序(默认按字典序)。

dump指令:快照,查看这一瞬间内存中的信息【面试点】

指令结合案例:

sudo:Linux中使用管理员指令。

在后面的目录下查询id为31177的线程,把查看的信息保存为后面路径下的dump17文件。

 用grep指令在dump17文件中筛选行里有java.lang.Thread.State的信息,然后统计完排序。


 死锁

 非静态方法必须依托对象的存在,在每个对象里都有一份。

synchronized(A)是对A加锁;加锁只能对引用类型加锁,锁住A后其他线程不允许读A ;

当进入睡眠状态时,即使时间片没执行完也必须让出CPU,并且不在就绪队列。【面试点】

 锁住A后什么时候会释放锁?---把{ }里的全部执行完才会释放

一个线程就算进入睡眠状态,也不会释放锁。【面试点】

sleep方法:让出CPU,但不释放锁

wait方法:让出CPU,并且释放锁 【面试点】

 加锁其实是给当前线程加上标记。

t1锁住A后沉睡,t2被操作系统选中锁住B后访问A失败,t2让出CPU进入阻塞队列,但锁仍未释放;2秒之后t1进入就绪队列,想要锁B失败,t1也进入阻塞队列------->死锁;

但不会消耗CPU,CPU仍可以执行其他任务,只是这两个任务永远执行不完了

竞争失败要进入阻塞队列;进入阻塞队列操作系统不会选中;

 恰巧的情况下,t2先执行,就不会出现死锁了。

 如何避免死锁?【面试点】

 

 

第二章、JAVA并发机制的底层实现原理

synchronize:重量级锁;读写都锁住---->读写都安全

volatile:轻量级锁(功能不全的锁);只能锁住写-->读安全,写不安全【面试点】

 volatile在多处理器(CPU里的多核心)开发中保证了共享变量的“可见性”----->当一个线程修改另一个共享变量时,另外一个线程能读到这个修改的值。

CPU要从内存中读取数据进行操作;内存中存储的是变量以及函数在它的运行状态下的数据;

CPU通过总线(本质上是导线)向内存中传输指令(1010指令就是电压信号);

单根导线上同一时刻只能过一个电压信号;总线的宽度一般是36-41位(并排三十多根到四十多根的导线);

一个指令通常先传地址,再传数据;

一次指令差不多是一个最基本的指令,也就是总线的宽度;不同的指令是不同同时传输的,不然电压信号会相互干扰;

指令在总线必须排队传输--->存在指令队列

内存和CPU的数据是双向的; 但CPU发送的指令比内存多,所以指令队列在CPU;

内存同一时刻只能被一个指令所指挥;

 假设现在有三个线程,分别对a,b,c for循环一万次,a通过总线读完+1再通过总线往回更新;

当a进行读写操作时,总线一直被占用,其他两个核心没办法操作;--->此时多核没意义

现在,CPU和内部的存储一直交互释放了总线,总线就可以把其他线程的数据读过来操作了--->支持了多核CPU的发展

a加到1万返回内存,在总线上一来一回,对总线占用的不多;

所以CPU的存储越多,存的数据越多,对核心的支持也越多,对总线的压力降低越明显,CPU内部存储和性能关乎大;

长距离网络传输时,一根导线可以同时存在多个电压信号。

 

 加入中转站后效率大幅度提高,但转发也损耗时间,不是越多越好,平衡点性能最好的平衡点是三级缓存。

多级缓存(L1/L2/L3)通过 减少CPU核心的等待时间 和 优化数据局部性,显著提高核心利用率。

高速缓存的并发性问题

现在有两个线程操作同一个变量a,线程1拿到时间片,将a加到10 ,还没往回更新时时间片到期,停止运行,线程2拿到时间片,将a加到10,往回更新,任务执行完释放CPU,线程1接着执行,往回更新---->此时两个线程一共加了20,但是最终的结果是10---->导致了相互覆盖问题。

高速缓存优点:增加传输效率,提升CPU利用率

高速缓存缺点:会导致数据相互覆盖

高速缓存往回更新是随机的,不一定计算完后往回更新;------->最终结果是不确定的

高速缓存往回更新时,高速缓存内的数据会清空,下一次计算需要重新从内存读取数据。


内存屏障:在内存中有一个变量标记,根据这个标记判断是否要访问。

缓存行:高速缓存的基本存储单元【一个缓存行64字节,没一个缓存行和外界相连的导线线路进行数据交互;】处理器填写缓存线时会加载整个缓存线,需要使用多个主内存读周期【一个周期从内存中读数据叫做一个主内存周期】

计算机存储区域:内存、硬盘、CPU内部的高速缓存---数据是一份份存储的,硬盘和内存中的数据单元是4KB

计算机中最小的物理单元是一字节一个存储单元,操作系统给硬盘和内存划分的逻辑存储是4KB一个存储区域-----在硬盘上,一个存储单元是栈区,在内存中一个存储单元是一个页,在高速缓存中存储单元是一个缓存行64字节。

一个缓存行可以存多份不同任务的数据,其中一个任务占着线操作时,其他任务就不能对它操作了--->所以每个任务过来操作时,相当于独占整个缓存行(不同芯片不一样,但每个都是独占的),造成一定性能损耗。

原子操作:不可中断的一个或一系列操作(一系列操作要么都成功,要么都失败,其中一个步骤如果失败,其他步骤都要还原)

在电脑底层只存在C语言的基本类型数据(6种基本数据:short  int  long bool double char ),其他语言在电脑内部都会转成C语言;

缓存行主要用于存储六种基本类型的数据,其容量从一字节到八字节不等。由于缓存行可容纳大量数据,当多个进程同时进行读写操作时,容易造成拥堵现象。具体而言,缓存行中存储的数据越多,发生排队的概率就越大;反之,数据量越少,排队概率则越小。

为优化性能,我们可采取以下策略:首先,尽量在缓存行中存储体积较大的数据,以减少数据总量,从而降低竞争概率。其次,在存储数据时,可采用独占式填充策略,即用自身数据填满整个缓存行。这种做法的优势在于,当需要读取数据时,可以避免竞争,实现快速访问,从而显著提升性能。

然而,这种优化策略的适用性取决于数据规模。在数据量较少时,这种方法是可行的;但当数据量较大时,若缓存行中存储的数据过少,反而会导致整体性能下降。因此,缓存行的设计需要权衡利弊,这也是为什么标准缓存行大小通常设定为64字节。过大的缓存行会增加拥堵风险,反而降低读取效率,有时甚至不如直接从内存读取。

对于追求极致性能的场景,可以将整个64字节缓存行用于存储单一数据。这种独占式存储方式能够确保每次读取都畅通无阻,达到最佳性能表现。

缓存命中:高层缓存容量有限,仅能存储少量数据。当核心处理器需要访问数据时,首先会查询高速缓存。若所需数据存在于缓存中,则直接返回,这种情况称为缓存命中;若缓存中不存在,则需访问内存,这种情况称为缓存未命中。由于缓存空间有限,部分数据可能存储在缓存中,而另一部分则不在,因此每次访问都需要先检查缓存是否存在所需数据,再决定是否访问内存。这一过程就是缓存命中的基本原理。

 写命中:------>针对高速缓存

当数据写回到内存缓存区域时,系统会首先检查该缓存地址是否存在于缓存中。以读取变量a为例:首先读取a,然后对a进行操作,操作完成后再将结果更新回缓存。在这个过程中,如果缓存中存在a的地址,称为写命中;如果不存在,则称为写未命中。值得注意的是,由于高速缓存空间有限,之前存储的数据可能已被清理。如果数据已被清除,在写回时就无法找到对应的缓存地址,从而导致写未命中。

写缺失: -------->针对内存

举例:a=5往回更新时,可能有其他线程把内存中的a删除,内存中的a没了,这种情况就是写缺失。

 【以上术语只有原子操作、缓存命中是学术性术语,其他几个面试面不到】


x86处理器是指CPU的内部结构;以下内容以x86处理器为主:

主流CPU处理器: x86架构、ARM架构【军队政府学校】、RISC-V 架构

 new的对象被volatile修饰,转换成汇编代码,里面有lock指令;

有volatile变量修饰的共享变量进行写操作时会多出第二行汇编代码---->CPU架构

 

当内存中的a被volatile变量修饰时,其中一个a往回更新时,另一个在高速缓存中的a失效 ;

但是用volatile修饰后还是会出错,如图,当a=10进入队列时,时间片到期,a=20执行指令,高速缓存中的数据失效,但问题是数据已经进入队列,所以仍会出现覆盖问题。

volatile并不能保证准确性。

补充:内存图角度理解并发编程

进程:正在执行的程序,内存为正在执行的程序开辟内存空间

线程:程序的执行过程,线程的执行依赖方法的不断入栈出栈

内存会为正在运行的程序开辟内存空间;

方法区:存储类信息

对象在堆里开辟空间

只有争抢的资源需要加锁,不争抢不用加锁

synchronized不能对变量进行加锁

锁是要加在被争抢的资源上的

一个完整的操作不能分开加锁

synchronized修饰静态方法:锁定的是非静态方法:锁方法的调用者,修饰代码块:锁定传入对象

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

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

相关文章

《 指针变量类型与内存访问:揭秘背后的奥秘》

🚀个人主页:BabyZZの秘密日记 📖收入专栏:C语言 🌍文章目入 一、指针变量类型的基本概念二、指针类型与内存访问字节数的关系(一)整型指针(二)字符型指针(三&…

mapbox进阶,使用mapbox-plugins插件加载饼状图

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.1 ☘️mapboxgl.Map style属性二、🍀使用mapbox-plugins插件加载饼状图1. ☘…

GraphicLayer与BusineDataLayer层级控制

补充说明: 当参与层级控制的元素是点型元素时,是无法参与ZIndex层级控制的,此时可以换个解决方案 1.给不同的高度值实现,元素间的层级控制覆盖 import * as mars3d from "mars3d"export let map // mars3d.Map三维地…

uniapp 百家云直播插件打包失败

打包错误日志 Android自有证书 打包失败 错误日志: https://app.liuyingyong.cn/build/errorLog/cf41a610-effe-11ef-88db-05262d4c3e5d原因:需要导入插件依赖 依赖地址:https://ext.dcloud.net.cn/plugin?id16289 百家云直播插件地址 直播插…

【C++】”如虎添翼“:模板初阶

泛型编程: C中一种使用模板来实现代码重用和类型安全的编程范式。它允许程序员编写与数据类型无关的代码,从而可以用相同的代码逻辑处理不同的数据类型。模板是泛型编程的基础 模板分为两类: 函数模板:代表了一个函数家族&#x…

十五、多态与虚函数

十五、多态与虚函数 15.1 引言 面向对象编程的基本特征:数据抽象(封装)、继承、多态基于对象:我们创建类和对象,并向这些对象发送消息多态(Polymorphism):指的是相同的接口、不同的…

点云特征提取的两大经典范式:Voxel-based 与 Pillar-based

点云特征提取的两大经典范式:Voxel-based 与 Pillar-based 在点云处理领域,尤其是针对 3D 目标检测任务,特征提取是核心环节之一。目前,Voxel-based(体素化)和 Pillar-based(柱状化&#xff09…

前苹果首席设计官回顾了其在苹果的设计生涯、公司文化、标志性产品的背后故事

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

web 自动化之 selenium 元素四大操作三大切换等待

文章目录 一、元素的四大操作二、三大切换&等待1、切换窗口:当定位的元素不在当前窗口,则需要切换窗口2、切换iframe:当定位的元素在frame/iframe,则需要切换3、切换弹出窗口 一、元素的四大操作 1、输入 2、点击 3、获取文本 4、获取属…

window server 2012安装sql server2008 r2

执行sql server2008 r2安装目录下的setup 选择运行程序而不获取帮助 然后就是让人绝望的 只能先搞这个了,F*微软,自家软件不让正常安装 打开服务器管理器->添加角色和功能->选择Web 服务(IIS)->添加.NET Framework3.5 然…

【K8S学习之生命周期钩子】详细了解 postStart 和 preStop 生命周期钩子

0. 参考 Kubernetes容器生命周期 —— 钩子函数详解(postStart、preStop) - 人艰不拆_zmc - 博客园详解Kubernetes Pod优雅退出 - 人艰不拆_zmc - 博客园 1. Kubernetes 生命周期钩子概述 在 Kubernetes 中,生命周期钩子(Lifec…

测试文章标题01

模型上下文协议(Model Context Protocol, MCP)深度解析 一、MCP的核心概念 模型上下文协议(Model Context Protocol, MCP)是一种用于规范机器学习模型与外部环境交互的标准化框架。其核心目标是通过定义统一的接口和数据格式&am…

kubuntu系统详解

Kubuntu 系统深度解析(从系统架构到用户体验) 一、定位与核心特性 Kubuntu 是 Ubuntu 的官方 KDE 衍生版,基于 Ubuntu 的稳定底层(Debian 技术栈),搭载 KDE Plasma 桌面环境,主打 “功能丰富、…

cURL:通过URL传输数据的命令行工具库介绍

文章目录 1. 什么是 curl?2. 下载与安装 curl3. curl 的常见用法3.1 获取网页内容3.2 下载文件3.3 发送 POST 请求(带表单数据)3.4 发送带 JSON 的 POST 请求 1. 什么是 curl? cURL(CommandLine URL)是非常…

从零搭建AI工作站:Gemma3大模型本地部署+WebUI配置全套方案

文章目录 前言1. 安装Ollama2.Gemma3模型安装与运行3. 安装Open WebUI图形化界面3.1 Open WebUI安装运行3.2 添加模型3.3 多模态测试 4. 安装内网穿透工具5. 配置固定公网地址总结 前言 如今各家的AI大模型厮杀得如火如荼,每天都有新的突破。今天我要给大家安利一款…

Element Plus对话框(ElDialog)全面指南:打造灵活弹窗交互

📌 开篇导语 对话框是Web应用中实现用户交互的核心组件之一,常用于信息确认、表单提交或详情展示。Element Plus的ElDialog组件以高扩展性和优雅动效著称,支持高度定制化开发。本文将从基础配置到进阶技巧,手把手教你掌握对话框组…

解决WSL、Ubuntu的.ico图标不正确显示缩略图

解决WSL、Ubuntu的.ico图标不正确显示缩略图 问题描述 Win10系统中由于更新了某些软件,篡改了默认的图像显示软件,导致WSL等软件未能成功显示图标,表现如下: 解决方法 将ico文件的默认打开方式更改为“画图”,如下…

[数据结构高阶]并查集初识、手撕、可以解决哪类问题?

标题:[数据结构高阶]并查集初识、手撕、可以解决哪类问题? 水墨不写bug 文章目录 一、认识并查集二、模拟实现并查集三、用并查集解决问题1、[省份的数量](https://leetcode.cn/problems/number-of-provinces/)2、[等式方程的可满足性](https://leetcode…

如何快速入门大模型?

学习大模型的流程是什么 ? 提示词工程:只需掌握提问技巧即可使用大模型,通过优化提问方式获得更精准的模型输出套壳应用开发:在大模型生态上开发业务层产品(如AI主播、AI小助手等),只需调用API…

《AI大模型应知应会100篇》第59篇:Flowise:无代码搭建大模型应用

第59篇:Flowise:无代码搭建大模型应用 摘要:本文将详细探讨 Flowise 无代码平台的核心特性、使用方法和最佳实践,提供从安装到部署的全流程指南,帮助开发者和非技术用户快速构建复杂的大模型应用。文章结合实战案例与配…