在读一篇文章的时候,作者对volatile的使用场景就生动解释了它的使用误区:
不要在依靠volatile变量现有值的情况下对其自身做修改
其实就是不要拿volatile变量做多步骤的操作就是了。
volatile变量单纯的读或写操作是可以的,例如:
volatile int count = 1;
单纯的写操作,直接将value赋值给count,不用读取count变量。
这种操作相等于带锁的原子操作,因为其本身就是不可再分割的最小操作了。
public void inc(int value) {
count = value;
}
直接读取count变量返回。
public int get() {
return count;
}
像 count++ 这种是复合操作。先读取count,再计算,再写回主存。
这篇文章的情景类似:
Java Concurrency: AtomicReference - DZone Java
volatile不是保证变量最新值么,我一开始不理解,就是count被读入a cpu的cache中,在另外一个b cpu修改了值且写回主存后,由于缓存一致性协议的作用,会使a中的cache的count无效,a再做计算时应该重新读主存。
我思索良久,查了些资料。我想能解释这种现象只能是这样了:
我们知道 cpu 执行代码都是执行代码被编译之后的指令。
cpu都是顺序执行,a先把count读到cache,b紧接着计算了count写回了主存,并且使其他cpu中的cache的count无效。但是同时 a 也在向下执行,执行到calculate指令,计算完毕后再写回主存。就发生了覆盖问题。
通过这种推理可以发现,如果a在 load 指令的时候发现 count 无效了,会从主存再次 read,但是a已经过了load指令,就不会再次检查变量的有效性问题了。
所以volatile变量只保证每次读取变量时的可见性,所以在使用变量的过程中,有可能此时变量就已经被其他线程修改了!!!
read 从内存中读值存入CPU的高速缓存
load 将数据加载进CPU计算
store CPU将值存到高速缓存
write 将数据从高速缓存写回内存