volatile关键字和CAS

来源:互联网 发布:百度指数如何导出知乎 编辑:程序博客网 时间:2024/06/16 02:15

volatile关键字的两层语义

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
2)禁止进行指令重排序。

volatile保证值写入后,其他线程能立即读到修改的值,即在写入值后下次读取之前会将工作内存中已经修改的值同步到主存中,这是“可见性“。但是已经读取到工作内存中的值不会进行同步。

即使加了volatile关键字,多个线程去修改这个volatile变量时,还是可能会出现非预期值。因为修改变量可分为几个步骤(1.读取值到寄存器1,2.cpu高速缓存修改为新值,新值保存到寄存器2,3.写入寄存器2的值到主存),线程1走完了步骤2还未执行步骤三时,即使此时有线程2修改了volatile变量的值,也只是会同步寄存器1的值不会同步寄存器2的值,所以线程继续将寄存器2的值写入主存,导致两次对volatile变量的操作不是叠加而是覆盖。

作者:xyzZ
链接:https://www.zhihu.com/question/49656589/answer/117826278
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
volatile 只能保证 “可见性”,不能保证 “原子性”。count++; 这条语句由3条指令组成:
(1)将 count 的值从内存加载到 cpu 的某个寄存器r
(2)将 寄存器r 的值 +1,结果存放在 寄存器s
(3)将 寄存器s 中的值写回内存
所以,如果有多个线程同时在执行 count++;,在某个线程执行完第(3)步之前,其它线程是看不到它的执行结果的。
在没有 volatile 的时候,执行完 count++;,执行结果其实是写到CPU缓存中,没有马上写回到内存中,后续在某些情况下(比如CPU缓存不够用)再将CPU缓存中的值flush到内存。正因为没有马上写到内存,所以不能保证其它线程可以及时见到执行的结果。
在有 volatile 的时候,执行完 count++;,执行结果写到CPU缓存中,并且同时写回到内存,因为已经写回内存了,所以可以保证其它线程马上看到执行的结果。但是,volatile 并没有保证原子性,在某个线程执行(1)(2)(3)的时候,volatile 并没有锁定 count 的值,也就是并不能阻塞其他线程也执行(1)(2)(3)。可能有两个线程同时执行(1),所以(2)计算出来一样的结果,然后(3)存回的也是同一个值。
补充几篇资料:
(1)java 内存模型:Java Memory Model
(2)java volatile 关键字:Java Volatile Keyword
(3)使用 volatile 的一些pattern:Java theory and practice: Managing volatility

CAS

volatile关键字只保证可见性,不保证原子性,这时如果使用同步锁会独占资源影响效率(这是一种悲观锁),那么就可以利用来实现乐观锁,在cpu中修改值以后,先比较cpu中开始读取的值B是否与变量当前值A一致,如果一致则将修改的值C保存到变量中覆盖A,如果不一致则返回修改失败,则重新读取变量的值再进行这样的操作直到成功,这既是CAS,Compare and Swap,比较然后交换。
java的java.util.concurrent.atomic包的类就是CAS的具体实现。

原创粉丝点击