不中断就能保证原子性?大错特错!

不中断就能保证原子性?大错特错! 如果读者您对“原子性”的认知单纯只是一组操作要么连续做完要么不做的话那么说明您不是真的懂原子性这篇文章值得您一读。很多人包括笔者自己在刚接触并发领域时通常会把“连续性”等同于“原子性”认为对于一系列的操作只要这一系列操作能直接执行完在此期间不被打断这一系列操作就是原子性的。其实这种看法是错误的。这种情况只能说明这一系列操作具有“连续性”而不具备“原子性”。真正的原子性在具备连续性的同时还应该具有“不可窥探性”。“原子性的最终保障必须来自硬件软件只能借助硬件原语来封装和扩展原子性边界”在软件层面讨论原子性是没有意义的要讨论原子性 我们必须把目光下放到硬件层面。那么硬件是怎么实现原子性的? 让我们走进CPU内部看看真正的原子性实现剖析 CPU 的原语看 CPU 怎么实现原子性一. 连续性的实现在 CPU 的芯片外壳上通常有几根物理引脚是用来接受中断信号的外部设备想中断 CPU 就给这根引脚发送一个高电平信号。CPU 内部有一个控制节点它决定了中断是否能被响应这个控制节点就是一个“与门”有两个输入端一个是中断引脚信号另一个是中断屏蔽位。输出端为触发中断处理信号。当 CPU 执行原子操作时将中断屏蔽位置为 0在整个原子操作执行完后再置为 1。这就保证在整个过程中CPU 都不会响应外部中断直到原子操作结束。二. 不可窥探性的实现先来看一段Java代码public class TestAtomic { private static AtomicInteger count new AtomicInteger(0); public static void main(String[] args) { // !!! java的原子操作最终是由CPU的原子操作保障的 for (int i 0; i 100000; i) { count.incrementAndGet(); } System.out.println(Final count: count.get()); } }其汇编后的核心代码可以看到CPU 核心在执行原子性操作时会给指令加上“lock”前缀。该前缀会触发“缓存行锁定”(若数据跨缓存行则退化为锁总线)。通过 MESI 协议该核心将对应缓存行标记为Modified或Exclusive状态同时强制其他核心中对应的缓存行立即失效。在操作完成前其他核心若尝试读取该数据其请求会被“总线嗅探”机制拦截并等待直到当前核心完成写入并结束锁定状态。这种机制确保了外界无法拿到正在被原子性修改的“中间态”数据。原子性的权威定义为“全有或全无”无中间态、无部分执行聪明的您应该想到了真正的原子性不仅要依靠不被中断来实现“全有或全无无部分执行”的连续性还需要“互斥性”来实现“无中间态”的不可窥探如果外界要访问一个正在被原子操作修改的值 那么应该等待这个原子操作结束很多人也喜欢把原子性当成互斥性来看待其实这是不准确的严格来说互斥性只是实现原子不可窥探性的一种手段。结语如果这篇文章能给读者您带来一些启发和思考笔者将不胜荣幸。最后笔者有个问题想留给各位许多编程语言都宣称其锁机制能保障操作的“原子性”您认为这种“原子性”是真正的“原子性”吗欢迎在评论区分享你的观点。