- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
我是一个线程,一个卖票程序的线程.
自从我们线程诞生以来,同一个进程地址空间里允许有多个执行流一起执行,效率提升的同时,也引来了很多麻烦.
我们卖票线程的工作很简单,比如票的总数是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:在信号量一节中,实际上数据库能承受的并发量远不止这点,这里为了故事情节需要,弱化了数据库的并发承受能力.
这玩意儿能逆向分析代码,我直接麻了! 。
完了!CPU一味求快出事儿了! 。
核弹级漏洞!我把log4j扒给你看 。
可怕!CPU暗藏了这些未公开的指令! 。
我是Redis,MySQL大哥被我害惨了! 。
最后此篇关于【趣话计算机底层技术】一个故事看懂各种锁的文章就讲到这里了,如果你想了解更多关于【趣话计算机底层技术】一个故事看懂各种锁的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我要在这里做的是遍历Storybook故事,以便可以对它们进行视觉回归测试: const puppeteer = require('puppeteer'); const { toMatchImageS
有人可以帮我解释一下涉及老虎机的 Rasa 故事吗: ## story with email * intent_request_email - utter_request_email * in
我们能够实现与 Instagram Stories 的共享,但无法实现与 Facebook Stories 的共享,如下 these指示 。尽管已安装并更新 Facebook 应用,Android 仍
我在使用 Facebook 开放图表故事时遇到问题。我只想发布一个带有对象标题的故事。我尝试使用开放图形对象调试器,我可以清楚地看到 og:title 已定义,但我发布的提要中没有标题。 我也尝试使用
如何将数据从 .properties 文件动态加载到 jBehave 故事文件?我一直在我的故事文件中使用 $ ,例如: When the stock is traded at price : $ T
我是 jbehave 的新手 我正在尝试通过 junit 执行 jbehave。 jbehave 似乎只从类路径加载故事文件。是否可以将其配置为从用户定义的路径中获取故事文件。 public clas
我们刚刚开始考虑使用 JBehave 进行验收测试,我想知道使用它的人是如何组织故事的编写和故事文件的存储的。目前只是在开发它们,所以我们将故事文件与实现它们的 Java 代码一起存储在资源文件夹中。
我正在尝试按顺序运行 jbehave 故事。 我的集成测试包结构如下所示 src/it/some/package/name/packageA/a.story src/it/some/package/n
我有一个 jBehave 故事如下; Scenario: Setup test GivenStories: common/story_1.story, common/sto
有人可以帮我运行一个 JBehave 故事吗?我在 Eclipse 中有一个 Maven 项目。 故事是: Meta: @author Nikolay Vasilev @bdd-talk: BG JU
我们有一个 Web 开发项目团队,我们决定使用单个存储库及其优缺点。我们将待办事项分成 Jira 故事,每个故事都有几个 FE 和 BE 任务。喜欢: ABC-1 显示猫 1.1. ABC-2 [FE
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引起辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the he
在我们公司,我们目前正在尝试建立故事 map (Jeff Patton)的概念。 我有以下问题:- 在具有交叉问题/任务的故事 map 中会发生什么?我们有贯穿整个项目的事件。故事 map 中的这些事
我想获得与远程 GIT 和 SVN 存储库同步的本地 GIT 存储库。 我正在执行的步骤如下: > git push Everything up-to-date > git pull Already
目前我所有的 .stories 文件都保存在 stories 目录中。随着我添加更多组件,它会增长。 相反,我想根据 Storybook 站点上的文档在组件级别添加每个组件的 .stories 文件:
我在网上读到 snapchat 和 instagram 有不同的方法来捕获视频,而不是在 Android 中使用相机 API,这允许它们向捕获的内容添加过滤器/贴纸等。 任何人都可以建议解决这个问题的
在 let returnTrip = Page(story: .ReturnTrip) 中它给了我们错误。我真的不知道为什么1 enum Story { case ReturnTrip (String
我们刚刚开始将Jira(带有Jira Agile插件-Scrum模板)用于我们的问题跟踪程序和敏捷计划。 我对故事和改进之间的差异(或预期的差异)感到困惑。我们所有的“东西”都写成故事。这些故事描述了
我正在为一个网站设计一个数据库,该数据库将至少表示 4 种不同的对象类型(文章、博客文章、照片、故事),每一种都有足够不同的数据要求来保证他们自己的表。我们希望用户能够发表任何这些类型的评论。评论的数
就目前情况而言,这个问题不太适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、民意调查或扩展讨论。如果您觉得这个问题可以改进并可能重新开放,visit
我是一名优秀的程序员,十分优秀!