一个故事就能够教会你看懂各种锁

我是一个线程,一个卖票程序的线程。

自从我们线程诞生以来,同一个进程地址空间里允许有多个执行流一起执行,效率提升的同时,也引来了很多麻烦。

我们卖票线程的工作很简单,比如票的总数是100,每卖一张就减1,直到变成0售完为止。

以前单线程的时候没啥问题,但多个线程一起执行的时候就发现,有些家伙读取到票数是100,减1后变成99,还没等他把票数写回去,另外有别的线程去读也是100,也做了同样的事,结果卖了两张票,票数才减了1张,一天下来,多卖了很多票,气的人类差点想砸了我们。

原子操作

我们把这问题反馈给了操作系统大哥,他给我们的解决方案是:读取票数->票数减1->写回票数这三个步骤不能被拆分,中途不能被打断,他说这个叫原子操作

他给了我们一套原子操作的手册,里面不止有减法,还有加法、位运算,只要调用手册里的原子操作函数,就能保证逻辑的正确性。

我很好奇操作系统大哥是如何实现这个过程的原子化的,他告诉我,如果CPU只有一个核很好办,执行原子操作的时候,他不切换线程就可以。而如果是有多个核,就需要CPU来帮忙了。

你还别说,我们用这个原子操作来卖票后,再也没有发生超额卖票的问题。

自旋锁

有一天,我们卖票程序进行了升级,不再是直接读取票数->票数减1->写回票数这么简单,还需要安排座位,现在变成了:

 我们一翻手册,没有哪个原子操作函数能满足我们的功能,毕竟安排座位这个操作是咱们卖票程序自己的事儿,一点也不通用,操作系统大哥肯定不会专门为我们开发一个原子操作函数。

我们只好再一次求助操作系统大哥,他一看就说:“你们这个问题,用自旋锁就可以解决”

锁?我们还是第一次听说这玩意,不知道是什么意思。

操作系统告诉我们,让我们回去创建一个锁,这锁里面有一个状态标记来表示当前有没有被占用,所有线程在执行卖票操作之前,都得先去获取这个锁,如果锁被占用了,线程就会阻塞在获取函数那里,获取函数内部会不断循环去检查,直到别的线程释放后才返回。

 因为获取锁的时候线程会一直循环检查状态,所以这锁也叫自旋锁。

现在,我们的工作流程变成了:

我们又可以愉快地卖票了!

互斥锁

我们的业务发展很快,后来,我们用上了数据库,把票的数量写到了数据库里面,于是我们的工作流程变成了:

 本以为只是把票数从本地内存搬到了数据库,应该没什么不一样,结果发现我们运行经常出错,还莫名其妙地被杀掉进程。

我们向操作系统大哥大倒苦水,没想到他却说:“你们还好意思诉苦,你们获取自旋锁后搞那么耗时的操作,让别的线程一直自旋等待,把CPU都跑得飞起,风扇都转个不停···”

我们都羞愧地低下了头,原来,把票数从本地内存搬到了数据库,差别这么大。

操作系统又接着说道:“自旋锁因为会使得线程一直阻塞自旋,没有让出CPU,所以只适合处理比较快速的场合,像读取数据库这种很耗时的操作,不能用它,会白白浪费CPU时间!”

我们又询问:“有没有别的不浪费CPU的办法呢?”

操作系统大哥又给我们介绍了一个叫互斥锁的东西,听说获取这个锁的时候,线程不会去自旋检查,而是把自己放到这把锁的等待队列中,然后就交出CPU执行权限,进入睡眠,看起来就跟阻塞一样,等到后面别的线程释放锁之后,再去唤醒它的等待队列里的线程继续运行。

回去以后我们就用上了互斥锁,现在我们的流程变成了这样:

 我们又又能愉快地卖票了!

条件变量

有一天,我们的卖票程序又进行了升级,21-100号票价格比较便宜,交给其他线程来卖,1-20号票价格比较贵,交给我来卖。

现在,我们不同的线程卖的票不一样了。

别的线程的流程是这样:

 而我的流程是这样:

 使用互斥锁倒也没什么问题,可就是我经常拿到锁以后发现票号还大于20,不该我处理,只好默默的释放锁,白白把我唤醒,却什么也没干!

空手而回的次数多了以后,我又去请教操作系统大哥,能不能让我指定一个条件,等条件满足了再唤醒我运行,别让我白跑。

没想到还真有办法!操作系统告诉了我一个叫条件变量的东西,等待条件变量的线程平时阻塞着,别的线程发现条件满足之后,就将条件变量激活,那个时候等待的线程才会被唤醒。

回去之后,我跟我的小伙伴儿们商量了一下,我们创建了一个条件变量,等到它们发现票号小于等于20的时候,就把条件变量激活,我就会被唤醒,再也不用白跑了!

信号量

互斥锁和条件变量真是好东西,帮了我们大忙,不仅帮我们解决了卖票的问题,我们还在其他很多地方使用它,我们遇到的绝大多数同步和互斥问题都可以用它们来解决。

直到有一天,我们遇到了一个新的问题。

我们的票越卖越好,从100到1000,票的数量越来越多,来找我们买票的客户也越来越多。

因为每次售票都要访问数据库,连接它的线程有些多,那家伙有些吃不消了。

希望我们控制一下访问数据库的线程数量。

我们很自然的想到了互斥锁,只有拿到锁的线程才能去访问数据库。

可这互斥锁名叫互斥,只能允许一个线程拿到锁,总不能只允许一个线程访问数据库吧,那可不行。所以我们希望这个名额能放宽,允许多个线程同时获得锁。

我们再一次找到了操作系统大哥,大哥拿出了他的绝招——信号量

他告诉我们,这信号量就像一个升级版的互斥锁,它里面有一个计数器,可以用来指定最多允许多少个线程同时获得它。

这正是我们想要的锁!

很快我们用上了信号量,我们又又又能愉快地卖票了!

Tips:在信号量一节中,实际上数据库能承受的并发量远不止这点,这里为了故事情节需要,弱化了数据库的并发承受能力。

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

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

相关文章

LabelImg下载及目标检测数据标注

为什么这一部分内容这么少会单独拎出来呢,因为后期会接着介绍YOLOv8中的其他任务,会使用其他软件进行标注,所以就单独区分开来每一个任务的标注方式了。 这一部分就介绍目标检测任务的标注,数据集是我从COCO2017Val中抽出来两类&a…

H5视频付费点播打赏影视系统程序全开源运营版

这是一款视频打赏源码,勿做非法用途,由用户亲测功能完善,源码仅用于学习使用,分享链接是用户云盘,具有时效性,感兴趣的可以去学习。 thinkphp开发,前后端分离设计,支持游客登陆、VIP…

经典的设计模式和Python示例(一)

目录 一、工厂模式(Factory Pattern) 二、单例模式(Singleton Pattern) 三、观察者模式(Observer Pattern) 一、工厂模式(Factory Pattern) 工厂模式(Factory Pattern…

牛客网刷题 | BC79 小乐乐求和

目前主要分为三个专栏,后续还会添加: 专栏如下: C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读! 初来乍到,如有错误请指出,感谢! 描述 小乐乐最近接触了求…

java-springboot项目添加swagger2/Knife4j,附注解

文章目录 添加依赖config工作包中新增SwaggerConfig报错注解 环境&#xff1a; jdk1.8 java8 springboot2.6.13 swagger2.9.2 添加依赖 pom.xml <!-- 添加swagger2--><dependency><groupId>io.springfox</groupId><artifactId>springfo…

函数编辑器调研及设计开发

前言&#xff1a;在产品研发中需要一款可嵌入web开发的代码及函数编辑器&#xff0c;本文从功能&#xff0c;扩展&#xff0c;外观/交互&#xff0c;维护/社区&#xff0c;兼容性&#xff0c;开源与否等方面考虑&#xff0c;进行对比筛选 1、编辑器统计数据 市面上编辑器有很…

【管理篇】如何提升管理中的沟通效率?

目录标题 管理沟通那些事如何提升沟通效率?&#x1f525;如何提升沟通技能&#xff1f; 向上沟通、员工激励和团队凝聚力提升 是管理沟通上比较难得问题 管理沟通那些事 管理沟通让技术管理者们痛苦的主因是确定性和规则性的减弱&#xff0c;不确定性的大幅度上升&#xff0c…

微软正在自主构建一个名为 MAI-1 的大型语言模型(不依赖 OpenAI)

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

IDEA中向Data Sources导入sql文件

IDEA中向Data Sources导入sql文件 开篇 在学习黑马的课程时&#xff0c;时常需要向数据库中导入sql文件生成数据库表&#xff0c;每次都会忘记导入步骤&#xff0c;折腾许久&#xff0c;于是将过程记录下来。 步骤 在Database中选择你要导入的数据库源&#xff0c;如图我想…

Matlab图像中加入脉冲噪声、高斯噪声并用均值滤波、中值滤波进行滤波处理

一、脉冲噪声和高斯噪声简介 脉冲噪声和高斯噪声是两种常见的信号干扰类型&#xff0c;它们的特性和影响各不相同&#xff1a; 脉冲噪声&#xff08;Impulse Noise&#xff09;&#xff1a; 在图像中&#xff0c;脉冲噪声表现为随机出现的亮点或暗点&#xff0c;这些噪声点通常…

[开发|鸿蒙] DevEco Studio编译构建(笔记,持续更新)

构建体系 编译构建是将应用/服务的源代码、资源、第三方库等&#xff0c;通过编译工具转换为可直接在硬件设备上运行的二进制机器码&#xff0c;然后再将二进制机器码封装为HAP/APP软件包&#xff0c;并为HAP/APP包进行签名的过程。其中&#xff0c;HAP是可以直接运行在模拟器…

FIFO Generate IP核使用——同步复位

在描述FIFO&#xff08;First In First Out&#xff09;或其他存储结构的同步复位&#xff08;Synchronous Reset&#xff09;功能时&#xff0c;srst&#xff08;或wr_rst/rd_rst&#xff0c;即写入和读取时钟域的同步复位信号&#xff09;仅适用于块RAM&#xff08;Block RAM…

企业为什么需要主数据管理工具?十大热门主数据管理工具盘点

主数据管理是一套综合性的策略和技术&#xff0c;用于协调和管理企业内用于识别关键业务实体&#xff08;如客户、产品、供应商和员工&#xff09;的一致性、准确性和统一性的数据。主数据管理的目的是创建一个“单一真相源”&#xff0c;确保在不同部门和系统之间共享的数据保…

AI预警未来:山体滑坡与塌方事故的潜在发现者

在科技日新月异的今天&#xff0c;人工智能&#xff08;AI&#xff09;的应用已经渗透到了我们生活的各个领域。而在防灾减灾的领域中&#xff0c;AI技术的引入无疑为我们打开了一扇新的大门。以梅大高速大埔往福建方向K11900m附近发生的路面塌方灾害为例&#xff0c;我们不禁思…

未授权访问:Redis未授权访问漏洞

目录 1、漏洞原理 2、环境搭建 3、未授权访问 4、利用redis未授权写入weshell 5、利用redis未授权反弹shell 6、利用redis未授权实现免密登录 防御手段 从这篇文章开始我就要开始学习各种未授权访问的知识和相关的实操实验了&#xff0c;一共有好多篇&#xff0c;内容主…

navicat premium16.3.9重置

软件下载 官网地址&#xff1a;https://navicat.com.cn/products/ # 准备脚本 1、建一个txt 2、复制以下代码 3、修改文件格式为bat 4、运行bat文件 5、重新打开navicat&#xff0c;试用期重置为14 经测试16.2.3以上版本均可用 echo off set dnInfo set dn2ShellFolder set r…

YOLOv8改进 | 主干篇 | 2024.5全新的移动端网络MobileNetV4改进YOLOv8(含MobileNetV4全部版本改进)

一、本文介绍 本文给大家带来的改进机制是MobileNetV4&#xff0c;其发布时间是2024.5月。MobileNetV4是一种高度优化的神经网络架构&#xff0c;专为移动设备设计。它最新的改动总结主要有两点&#xff0c;采用了通用反向瓶颈&#xff08;UIB&#xff09;和针对移动加速器优化…

用脚本写一个日期样式的字符

现在想要诸如此类样式的语句&#xff1a;&#xff08;过去三个月的&#xff09; 可以用python脚本写&#xff1a; from datetime import date, timedelta# 获取当前日期 current_date date.today()# 定义过去三个月的时间间隔 three_months_ago current_date - timedelta(da…

<网络安全>《79 概念讲解<第十二课 物联网常用协议-(远距离非蜂窝网络)-终端设备>》

协议简称全称名称内容说明ZigBee也称紫蜂低速短距离传输的无线通信协议一种高可靠的无线数传网络&#xff0c;主要特色有低速、低耗电、低成本、支持大量网上节点、支持多种网上拓扑、低复杂度、快速、可靠、安全。ZigBee技术是一种新型技术&#xff0c;主要是依靠无线网络进行…

用一个故事告诉你协程到底是什么

神秘使者 “久闻Java语言跨越平台&#xff0c;框架众多&#xff0c;不过二十年功夫&#xff0c;就已晋升天下第一编程语言&#xff0c;今日一见&#xff0c;果然名不虚传呐&#xff01;” “使者先生您过奖了&#xff0c;咱们快些走&#xff0c;国王陛下已经等候多时了” 今…