Volatile 原理六:图解指令重排
指令重排的例子
设想一下这种场景:定义了变量 num=0 和变量 flag=false,线程 1 调用初始化函数 init()执行后,线程调用 add()方法,当另外线程判断 flag=true 后,执行 num+100 操作,那么我们预期的结果是 num 会等于 101,但因为有指令重排的可能,num=1 和 flag=true 执行顺序可能会颠倒,以至于 num 可能等于 100
先看线程 1 中指令重排:
num= 1;flag = true;
的执行顺序变为 flag=true;num = 1;
,如下图所示的时序图
如果线程 2 num=num+5 在线程 1 设置 num=1 之前执行,那么线程 2 的 num 变量值为 5。如下图所示的时序图。
8.4 volatile 怎么实现禁止指令重排?
我们使用 volatile 定义 flag 变量:
如何实现禁止指令重排:
原理:在 volatile 生成的指令序列前后插入内存屏障
(Memory Barries)来禁止处理器重排序。
有如下四种内存屏障:
volatile 写的场景如何插入内存屏障:
在每个 volatile 写操作的前面插入一个 StoreStore 屏障(写-写 屏障)。
在每个 volatile 写操作的后面插入一个 StoreLoad 屏障(写-读 屏障)。
StoreStore 屏障可以保证在 volatile 写(flag 赋值操作 flag=true)之前,其前面的所有普通写(num 的赋值操作 num=1) 操作已经对任意处理器可见了,保障所有普通写在 volatile 写之前刷新到主内存。
volatile 读场景如何插入内存屏障:
在每个 volatile 读操作的后面插入一个 LoadLoad 屏障(读-读 屏障)。
在每个 volatile 读操作的后面插入一个 LoadStore 屏障(读-写 屏障)。
LoadStore 屏障可以保证其后面的所有普通写(num 的赋值操作 num=num+5) 操作必须在 volatile 读(if(flag))之后执行。
作者简介:悟空,8 年一线互联网开发和架构经验,用故事讲解分布式、架构设计、Java 核心技术。《JVM 性能优化实战》专栏作者,开源了《Spring Cloud 实战 PassJava》项目,公众号:`悟空聊架构`。本文已收录至 www.passjava.cn
版权声明: 本文为 InfoQ 作者【悟空聊架构】的原创文章。
原文链接:【http://xie.infoq.cn/article/99f26a2b940ecc1410aa42887】。文章转载请联系作者。
评论