java-并发编程Android开发经验谈Android开发

深入理解volatile的内存语义

2017-09-14  本文已影响227人  六尺帐篷

一旦一个共享变量(类的成员变量、 类的静态成员变量) 被 volatile 修饰之后, 那么就具备了两层语义:

内存可见性

那么, 在线程 2 修改 stop 值时(当然这里包括 2 个操作, 修改线程 2 工作内存中的值, 然后将修改后的值写入内存) , 会使得线程 1 的工作内存中缓存变量 stop 的缓存行无效, 然后线程 1 读取时, 发现自己的缓存行无效, 它会等待缓存行对应的主存地址被更新之后, 然后去对应的主存读取最新的值。

禁止重排序

volatile 关键字禁止指令重排序有两层意思:

为了实现 volatile 的内存语义, 加入 volatile 关键字时, 编译器在生成字节码时,
会在指令序列中插入内存屏障, 会多出一个 lock 前缀指令。 内存屏障是一组处理器指令, 解决禁止指令重排序和内存可见性的问题。 编译器和 CPU 可以在保证输出结果一样的情况下对指令重排序, 使性能得到优化。 处理器在进行重排序时是会考虑指令之间的数据依赖性。

内存屏障, 有 2 个作用:

lock 前缀指令在多核处理器下会引发了两件事情:

1.将当前处理器中这个变量所在缓存行的数据会写回到系统内存。 这个写回内存的
操作会引起在其他 CPU 里缓存了该内存地址的数据无效。 但是就算写回到内存, 如果其他处理器缓存的值还是旧的, 再执行计算操作就会有问题, 所以在多处理器下, 为了保证各个处理器的缓存是一致的, 就会实现缓存一致性协议, 每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了, 当处理器发现自己缓存行对应的内存地址被修改, 就会将当前处理器的缓存行设置成无效状态, 当处理器要对这个数据进行修改操作的时候, 会强制重新从系统内存里把数据读到处理器缓存里

2.它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置, 也不会把前面的指令排到内存屏障的后面; 即在执行到内存屏障这句指令时, 在它前面的操作已经全部完成。

内存屏障可以被分为以下几种类型:

上一篇 下一篇

猜你喜欢

热点阅读