volatile
是Java并发编程中一个很重要的成员,可以把它理解为一个轻量级的synchronized
。
volatile的作用有两个:
- 保证变量的内存可见性
- 防止指令重排序
保证变量的内存可见性
由于每个线程都有自己的本地线程缓存,当操作主内存中的数据时,会从主内存中复制数据到本地线程缓存中再进行操作。在多线程环境下,如果多个线程同时操作主内存中的同一块内存,就会造成多个线程间的数据不一致。
cpu采用一种缓存一致性协议(IMEI)来保证各个线程间缓存的一致性。
- 对volatile变量读写时,会触发cpu立即将本地线程缓存写回到主内存。
- 对volatile变量写入时,会导致其他线程缓存行失效,从而强制其他线程再次读取该变量时,从主内存中获取
通过以上两个规则,就可以保证在多线程环境下,每次读取到的数据都是最新一次写入都数据。
发展指令重排序
指令重排序发生在编译器和cpu执行器,是一种提高cpu执行效率的手段。在单线程环境下,JMM能够保证被重排序后的指令执行结果的最终一致性,但并不能保证多线程环境下最终结果的正确性,这就需要进行同步处理。
同步的方法有两种,一种是加锁,保证复杂操作的原子性,一种是volatile修改变量,保证单个变量读写的原子性。
以下面一段代码为例
private Singleton singleton;
....
int a = 1;
singletion = new Singleton();
int b = 2;
在没有volatalie修饰时,代码中的三个操作有可能会被乱序执行,同时,由于singleton = new Singleton();
是一个复合操作,真正的执行顺序有可能更加乱序,那么,假如 int a = 1
会影响singleton对象的正确性,如果被cpu乱序执行,int a = 1
出现在来singleton = new Singleton()
的后面,就会出现错误。
如果singleton被声明为volatile,那么,singleton = new Singleton()
这段代码的乱序优化并不会收到影响,而是会禁止singleton = new Singleton()
与其前后代码的乱序优化,也就是保证volatile变量的乱序优化不会与其他代码的乱序优化混淆在一起。
总结
volatile变量的读写可以看成是对单个变量读写对同步处理;
对一个volatile变量对读总能看到它对最后一次写;
对单个volatile变量对读写具有原子性;