自旋锁到底在“转”什么?从抢锁到 CPU 空转,讲透原理与适用场景
平时说到锁很多人脑子里第一反应都是没抢到锁那就等着。但自旋锁不一样。它的思路有点“轴”锁现在被别人拿着没关系我不睡我就在这儿一直试直到你把锁放出来。这就是“自旋”两个字的由来。听起来似乎很浪费 CPU但它偏偏又大量存在于操作系统、JVM、并发框架和底层组件里。原因也不复杂有时候等一会儿比睡一觉再醒来更划算。一、先用一个很简单的例子理解自旋假设公司只有一台打印机。小王正在打印一张纸你也要打印。这时候有两种做法。第一种发现打印机被占用 ↓ 回工位坐下 ↓ 等别人通知 ↓ 再走回来打印这很像普通的阻塞等待。另一种打印机被占用 ↓ 站旁边等几秒 ↓ 看看好了没 ↓ 没好再看一次 ↓ 好了马上接着用这就是自旋锁的感觉。区别就在于阻塞锁会让当前线程睡眠自旋锁不会它会一直尝试拿锁。二、自旋锁底层到底在做什么最简单的自旋锁可以先写成这种伪代码while (lock 1) { // 什么都不干一直检查 } lock 1; // 临界区代码 lock 0;意思非常直接lock 0代表锁空闲。lock 1代表锁已经被占用。线程来抢锁时如果发现lock 1就一直循环。这就是所谓busy waiting忙等。不过上面这段代码实际上是不安全的。因为两个线程可能同时看到lock 0然后一起执行lock 1这样两个人就同时进临界区了锁直接失效。所以真正的自旋锁必须依赖一种非常重要的 CPU 原子操作CAS也就是Compare And Swap比较并交换。三、CAS 才是自旋锁真正的底座CAS 可以简单理解成一句话如果这个值现在还是我预期的值就把它改掉如果已经被别人改了那我什么都不做。比如当前 lock 0线程 A 想拿锁。它执行CAS(lock, 0, 1)意思是如果 lock 现在还是 0 就改成 1如果成功线程A拿到锁如果失败说明别人先拿走了于是继续尝试。代码大概可以理解成while (!CAS(lock, 0, 1)) { // 自旋 }释放锁lock 0;所以一个最简化的自旋锁本质其实就是CAS ↓ 失败 ↓ 继续 CAS ↓ 失败 ↓ 继续 CAS ↓ 成功 ↓ 进入临界区这就是所谓的“自旋”。四、为什么不直接让线程睡觉问题就来了。既然没抢到锁那把线程挂起不就好了为什么还要浪费 CPU 一直循环关键在于线程睡眠和唤醒其实并不是免费的。假设线程 A 没拿到锁。如果使用阻塞锁可能经历线程A运行 ↓ 发现锁被占用 ↓ 线程进入阻塞状态 ↓ 操作系统调度其他线程 ↓ 保存线程A上下文 ↓ 过一会锁释放 ↓ 重新唤醒线程A ↓ 恢复线程A上下文 ↓ 继续运行这里涉及一个重要概念上下文切换。CPU 要保存和恢复寄存器程序计数器栈信息线程状态这些操作都有成本。如果对方只占锁几百纳秒或者几微秒那你刚把线程挂起来锁可能已经释放了。结果变成为了等一会儿反而付出了一次完整的线程切换成本。这就像你去厕所发现有人“里面有人。”如果对方还有 5 秒就出来你没必要坐电梯回办公室 ↓ 坐下 ↓ 再坐电梯回来站门口等一下反而更快。这正是自旋锁存在的意义。五、自旋锁真正赌的是什么自旋锁其实是在赌一件事我赌你很快就会把锁释放。如果赌对了线程A持锁 ↓ 线程B自旋几次 ↓ 线程A释放 ↓ 线程B立刻拿到非常划算。因为线程 B 根本没有发生阻塞和唤醒。但如果赌错了线程A持锁10秒线程 B 还在那里CAS CAS CAS CAS CAS CAS ……那就惨了。CPU 可能被它白白占满。所以自旋锁有一个非常明显的特点适合锁持有时间非常短的场景。六、为什么自旋锁特别适合多核 CPU假设只有一个 CPU 核心。线程 A 拿着锁A持有锁线程 B 开始自旋B疯狂检查锁问题来了。CPU 此时正在执行 B。那 A 怎么运行A 不运行就没办法释放锁。于是很可能出现B在等A释放锁 但B一直占着CPU A反而得不到运行机会所以在单核环境里自旋通常意义不大。但多核就不一样。比如CPU Core 1 → 线程A → 正在使用锁 CPU Core 2 → 线程B → 正在自旋这时候A继续执行 ↓ 很快释放锁与此同时B一直观察 ↓ 立刻拿到锁这才是自旋锁最理想的状态。所以自旋锁天然和多核 CPU关系非常密切。七、自旋锁最大的缺点它真的会烧 CPU假设线程A拿锁以后 执行了5秒线程 Bwhile (!CAS(...)) { }这 5 秒里它并不是睡着。而是在判断 失败 判断 失败 判断 失败 ……CPU 使用率可能直接上去。如果只有一个线程还好。如果100个线程同时竞争一把锁线程1 → 自旋 线程2 → 自旋 线程3 → 自旋 …… 线程100 → 自旋所有人一起抢 抢 抢 抢 抢CPU 就会花大量时间在无意义竞争上。所以自旋锁不是“比阻塞锁高级”而是适用于不同场景。八、实际系统一般不会让线程无限自旋工程里通常不会真的写while (true) { CAS... }然后一直转到天荒地老。更常见的策略是先自旋 ↓ 尝试几次 ↓ 还是失败 ↓ 再阻塞比如尝试1次 失败 尝试2次 失败 尝试3次 失败 ……达到一定次数以后算了不等了 ↓ 线程挂起这样就把两种方案结合起来了短时间等待 → 自旋 长时间等待 → 阻塞这其实比“永远自旋”聪明得多。九、自旋次数应该固定吗也不一定。更聪明的实现甚至会根据之前的情况调整。比如某个锁以前经常自旋3次左右就拿到了那系统可能觉得这个锁一般释放得挺快可以多等一会儿。但如果过去经常自旋半天都拿不到系统可能就会别浪费 CPU 了早点挂起吧。这种思路通常叫自适应自旋。核心意思就是自旋多久不一定固定而是根据运行情况动态决定。十、自旋锁和互斥锁到底怎么选可以把它们想成两个完全不同的等待策略。自旋锁没抢到我就在门口等特点不睡眠 不主动让出 CPU 响应快 但浪费 CPU适合临界区非常短 锁很快释放 竞争不是特别激烈 多核 CPU阻塞锁没抢到我先回去睡一会 有位置再叫我特点节省 CPU 但涉及阻塞和唤醒 可能有上下文切换成本适合锁可能持有较长时间 竞争激烈 等待时间不可预测所以两者并不存在谁绝对更快。真正决定性能的是等待时间 竞争程度 CPU核心数量 临界区长度十一、Java 里其实到处都有“自旋思想”Java 开发里可能很少自己手写一个SpinLock但自旋思想其实非常常见。比如AtomicInteger AtomicLong AtomicReference这些原子类大量依赖CAS典型逻辑就是读取旧值 ↓ 计算新值 ↓ CAS尝试更新 ↓ 失败 ↓ 重新读取 ↓ 再次CAS本质上就是一种CAS 重试。再比如很多高性能并发组件也会使用类似思路先快速尝试 ↓ 短暂自旋 ↓ 竞争严重后再阻塞因为真正高性能的并发设计通常不是简单选择一直自旋或者直接阻塞而是在两者之间找平衡。十二、还有一个细节自旋时最好别真的“死转”最粗暴的写法while (locked) { }虽然能转但并不友好。现代 CPU 和运行时通常会提供一些“提示”。Java 里有Thread.onSpinWait();它相当于告诉 CPU我现在正在自旋等待不是在做正常计算。底层可以利用类似PAUSE这样的 CPU 指令优化自旋行为。好处包括减少资源消耗 降低流水线压力 改善超线程环境表现所以真正的高性能自旋并不是傻乎乎地疯狂 while。而是尽量让 CPU 知道这个线程只是在短暂等待。十三、一个例子把自旋锁彻底串起来假设现在有两个线程。线程 A拿到锁 ↓ 修改一个计数器 ↓ 只需要几十纳秒 ↓ 释放锁线程 B 此时过来。如果采用阻塞发现锁被占 ↓ 线程挂起 ↓ 操作系统调度 ↓ A释放锁 ↓ 重新唤醒B ↓ B继续运行有可能线程切换的成本比真正等待锁的时间还大。换成自旋B第一次CAS → 失败 B第二次CAS → 失败 B第三次CAS → 成功整个过程可能很快就结束了。这里自旋非常划算。但如果 A 拿锁以后请求数据库 ↓ 调用远程接口 ↓ 睡眠3秒B还在那里自旋CAS CAS CAS CAS ……那就是典型的错误使用。所以判断自旋锁适不适合只需要问一个问题拿锁的人到底多久能出来写在最后自旋锁看起来像是一个很高级的并发概念其实它的思想特别直接锁暂时拿不到但我觉得你马上就会释放所以我先不睡原地再试几次。它的底层核心可以压缩成CAS ↓ 成功 → 拿锁 失败 ↓ 继续 CAS ↓ 继续尝试而它存在的真正原因是线程阻塞 线程唤醒 上下文切换本身也是有成本的。所以当等待锁的时间 线程挂起和重新调度的成本自旋往往更划算。但反过来如果锁长期不释放自旋 拿 CPU 时间不停做无用功这时候阻塞反而更合理。所以理解自旋锁以后会发现它背后其实是一种很典型的性能优化思想不要一发现资源不可用就立刻进入重量级等待而是先赌一次——它也许马上就好了。赌对了省掉一次昂贵的线程切换。赌错了就得及时收手。这也是为什么真正成熟的并发实现通常不是“只自旋”或者“只阻塞”而是在两者之间动态取舍。

相关新闻

Excel行列求和:两组快捷键搞定全表与跳空汇总

Excel行列求和:两组快捷键搞定全表与跳空汇总

​在日常处理Excel报表时,行列求和恐怕是出现频率最高的操作之一。很多人习惯用鼠标点击工具栏上的“自动求和”按钮,或者手工输入SUM函数再拖动填充,虽然也能得出结果,但效率总差那么点意思。其实,Excel内置了一组堪称…

2026/8/25 14:16:12 阅读更多 →
力扣高频SQL50题知识点

力扣高频SQL50题知识点

一、查询select product_id from Products where low_fatsY and recyclableY;select 输出 from 表名where 条件;select name from Customer where referee_id ! 2 or referee_id is NULL;对于条件为某一元素为NULL用“元素 is NULL”select name,population,area…

2026/8/25 14:16:12 阅读更多 →
day24_jsvmp后两个生成算法

day24_jsvmp后两个生成算法

一、在switch中插桩,先看大概日志信息有什么变化! “case”,a[F1],“stack”,Y看见array一直递增!数组变化【视频35分】 我们就知道大概要插桩什么了,比如chartCodeAt,String.FromChartCode,parseInt等等操…

2026/8/26 16:13:41 阅读更多 →

最新新闻

工业清洗剂安全性深度评测:从成分到实测的全方位验证

工业清洗剂安全性深度评测:从成分到实测的全方位验证

在工业生产和实验室环境中,化学试剂的选型往往直接关系到一线操作人员的安全底线。很多技术团队在引入新溶剂或清洗剂时,容易陷入“只看参数表”的误区,忽略了实际工况中复杂的物理化学反应。一旦选错材料,轻则导致设备表面腐蚀、…

2026/8/26 16:55:04 阅读更多 →
从一条直线到大模型输出一个token(十):首 token 诞生与 KV-Cache

从一条直线到大模型输出一个token(十):首 token 诞生与 KV-Cache

从一条直线到大模型输出一个token(十):首 token 诞生与 KV-Cache 建议先看:从一条直线到大模型输出一个token(九):输出矩阵与多层堆叠 上一篇拿到了输出矩阵,6 条约束链全部沉淀在最…

2026/8/26 16:55:04 阅读更多 →
食品加工技术课程概述

食品加工技术课程概述

0 走进“食品加工技术”课程 一、课程核心信息 1. 课程性质 食品加工技术是应用科学,融合化学、物理学、生物学、微生物学、食品机械与化工原理,遵循技术先进、经济合理原则,研究食品资源利用、生产、贮运等问题,实现食品生产合…

2026/8/26 16:55:04 阅读更多 →
泰坦尼克乘客生存预测与风险决策建模

泰坦尼克乘客生存预测与风险决策建模

泰坦尼克号生存预测竞赛是Kaggle平台上一个标志性的入门级项目,长期作为初学者接触机器学习竞赛流程、理解结构化数据建模的起点。该任务要求基于乘客的舱位、性别、年龄等特征,预测其在沉船事件中的生存状态,本质是一个典型的二分类问题。竞赛数据集规模小、特征类型丰富,…

2026/8/26 16:54:00 阅读更多 →
手写数字识别如何支撑文档数字化应用

手写数字识别如何支撑文档数字化应用

在数据科学与机器学习的学习路径中,理论与实践的结合至关重要。Kaggle 竞赛平台提供了大量贴近真实业务场景的数据与问题,其中“Digit Recognizer”竞赛因其经典性与明确的入门定位,成为无数学习者踏入计算机视觉领域的第一步。该竞赛基于著名的 MNIST(Modified National I…

2026/8/26 16:54:00 阅读更多 →
企业知识库怎样支撑GEO内容生成:字段分层与事实锁设计

企业知识库怎样支撑GEO内容生成:字段分层与事实锁设计

企业知识库怎样支撑GEO内容生成:字段分层与事实锁设计 企业资料很多,不等于知识库可用。把公司介绍、产品手册、公众号旧文和销售话术全部扔进一个文件夹,搜索时可能找得到句子,生成内容时却容易把公司名、品牌名、产品名和能力边…

2026/8/26 16:54:00 阅读更多 →

日新闻

Python random 模块常用函数详解:从入门到实战

Python random 模块常用函数详解:从入门到实战

目录 1. 引言2. 准备工作3. 基础随机函数4. 序列相关函数5. 随机种子与复现6. 实战案例7. 注意事项8. 常见问题与排查9. 总结 1. 引言 摘要: 本文系统介绍 Python 标准库 random 模块中最常用的随机数生成函数。内容涵盖基础随机函数(random()、unifor…

2026/8/26 0:00:40 阅读更多 →
《Microsoft Sql server 2008 Internals》读书笔记--第三章Databases and Database Files(2)

《Microsoft Sql server 2008 Internals》读书笔记--第三章Databases and Database Files(2)

《Microsoft Sql server 2008 Internals》索引目录: 《Microsoft Sql server 2008 Internals》读书笔记--目录索引 在上篇文章中,主要介绍了创建数据库的基本语法和FileGroup的初步知识。需要注意的是: 关于FileGroup 如果你的系统是用Raid设备直接存…

2026/8/26 1:18:18 阅读更多 →
政务AI智能体怎么建?三种模式、三步路径与四个误区

政务AI智能体怎么建?三种模式、三步路径与四个误区

政务AI智能体已经从概念试点阶段,转入了政务服务的常态化落地应用;在实际使用过程中,它能自主理解办事需求、辅助完成填报申报、开展材料预审,并联动多个系统协同作业,真正嵌入到政务办理的全流程当中。但在落地推进过…

2026/8/26 1:18:18 阅读更多 →

周新闻

[光学原理与应用-521]:对光的错误理解与纠偏

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/26 14:45:33 阅读更多 →
SIP通话转接原理与REFER方法实战解析

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/25 3:38:18 阅读更多 →
Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/26 14:46:37 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/26 3:50:20 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/25 10:31:12 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/26 1:24:05 阅读更多 →