参照《深入理解Java虚拟机-JVM高级特性与最佳实践》(周志明)第二版 12.3.3
特性一
**保证此变量对所有线程的可见性。**即当一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的。
误区:volatile变量对所有线程是立即可见的,对volatile变量所有写操作都能立刻反应到其他线程之中,换句话说,volatile变量在各个线程中是一致的(在各个线程的工作内存中,volatile变量也可存在不一致的情况,但由于每次使用前都需要先刷新,执行引擎看不到不一致的情况,因此可以认为不存在一致性的问题),所以基于volatile变量的运算在并发下是安全的。但是Java里面的运算并非原子操作,导致volatile变量的运算在并发下一样不是安全的。
package cn.pengld;
public class VolatileTest {
public static volatile int race = 0;
public static void increase(){
race ++;
}
private static final int THREADS_COUNT = 20;
public static void main(String[] args) {
Thread[] threads = new Thread[THREADS_COUNT];
for(int i=0;i < threads.length;i ++){
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
for(int j = 0;j < 1000;j ++){
increase();
}
}
});
threads[i].start();
}
while (Thread.activeCount() > 2){
Thread.yield();
}
System.out.println(race);
}
}
这段代码发起了20个线程,每个线程对race变量进行10000次自增操作,如果这段代码能够正确并 发的话,最后输出的结果应该是200000。读者运行完这段代码之后,并不会获得期望的结果,而且会 发现每次运行程序,输出的结果都不一样,都是一个小于200000的数字。
特性二
禁止指令重排序优化。
指令重排序是指CPU采用允许将多条指令不按程序规定的顺序分开发送各相应电路单元处理。但也不是指令任意重排,CPU需要能正确处理指令依赖情况以保障程序能得出正确的执行结果。
譬如指令1把地址A中的值加10,指令2 把地址A中的值乘以2,指令3把地址B中的值减去3,这时指令1和指令2是有依赖的,它们之间的顺序 不能重排——(A+10)2与A2+10显然不相等,但指令3可以重排到指令1、2之前或者中间,只要保证 处理器执行后面依赖到A、B值的操作时能获取正确的A和B值即可。