关于 volatile 我这里要好好总结一下。
volatile关键字 对一个变量修饰,起到了两个作用,立即可见和禁止指令重排序。
当把累加后的结果 进行赋值的时候, 能保证赋值操作 和 将值写回到主内存的之间过程内,不会插入访问指令。
但是add 操作的时候,而 这个原值 就已经是过期数据了 。
可以这样理解。几个线程 cache中同时存在着 i=1的值。然后同时进行累加。累加的过程不分先后,是并行执行。所以每个线程 都累加 到了2 。 这样肯定不是正确的结果。
所以 volatile 仅仅是做到了立即可见和防止指令重排序,而并非是并发安全。
volatile关键字 对一个变量修饰,起到了两个作用,立即可见和禁止指令重排序。
本质上 都是在该变量被赋值后 多了一条指令 lock addl $0x0,(%esp) 这个指令起了两个作用。
首先说立刻可见:
一个是带lock前缀的addl汇编指令,作用是本cpu的cache 立刻写入主内存(这里是把当前修改变量的虚拟机栈的内存模型认为是cpu寄存器和高速缓存这个模型来讲的,因为虚拟机和硬件系统本身的优化, 会让工作内存优先存储在寄存器和高速缓存中。),从而引起别的cpu无效化自身的cache。
正常来说线程间访问变量 应该都是通过主内存的,访问的变量是有缓存的,并不是每次都从主内存读的,只有当在工作内存对赋值进行赋值后,写入主内存,才会引起其他cpu内核的cache无效,而工作内存中该变量被修改(use和assign指令)以及从工作内存写入主内存是有延迟的(即store和write指令),所以在这个空档期,另一给线程一条读取该变量的指令被其他内核所执行, 导致读到了脏数据。
而这条指令的关键就在于, 对该变量赋值后,立刻对该变量进行store和write指令(具有原子性) 强制其他内核从主内存来访问数据。 这条指令的执行意味着 变量已经同步到主内存中了。这样就不会产生脏读。
禁止指令重排序:
指令重排是值cpu允许多条指令不按程序规定的顺序,分开给不同的电路单元处理,那么lock addl 这条指令就是一条内存屏障指令(具体内存屏障指令因处理器架构而异)。表明在一个方法内执行的代码所对应的汇编指令不允许重排序。volatile 不适用的场景:
运算结果依赖变量的当前值。 有其他状态变量共同参与不变约束。
仅通过字节码指令的分析:
当把累加后的结果 进行赋值的时候, 能保证赋值操作 和 将值写回到主内存的之间过程内,不会插入访问指令。
但是add 操作的时候,而 这个原值 就已经是过期数据了 。
可以这样理解。几个线程 cache中同时存在着 i=1的值。然后同时进行累加。累加的过程不分先后,是并行执行。所以每个线程 都累加 到了2 。 这样肯定不是正确的结果。
所以 volatile 仅仅是做到了立即可见和防止指令重排序,而并非是并发安全。