Java语言为了解决并发程序中存在的原子性、可见性、有序性问题,听过了一些列处理并发的关键字。如synchronized、volatile、final等,上一篇中讲解了synchornizedguan关键字的用法和原理等。在文本中,我们来分析另外一个关键字----volatile。
1:volatile:轻量级同步锁
volatile本身的意思就是“易挥发”的意思,其实就是不稳定的意思。其作用与锁有相同的地方:保证可见性和有序性。不同的是,在原子性方面仅能保证写volatile变量操作的yuan原子性,但没有锁的排他性;其次,volatile关键字的使用不会引起上下文切换(这正是volatile被称为”轻量级“的原因)。
volatile是一个变量修饰符,只能用来修饰变量,无法修饰方法和代码块。其用法比较简单,只需要在可能被多个线程共享的变量前加如该关键字即可。
以一个单例作为其使用Demo
package sync;
public class VolatileDemo {
private volatile static VolatileDemo volatileDemo;
private VolatileDemo(){}
public static VolatileDemo getVolatileDemo(){
if(volatileDemo == null){
synchronized (VolatileDemo.class){
if(volatileDemo == null){
volatileDemo = new VolatileDemo();
}
}
}
return volatileDemo;
}
}
如以上代码,是一个比较典型的使用双重锁校验的形式实现单例的,其中使用volatile
关键字修饰可能被多个线程同时访问到的volatileDemo。
2:volatile可见性
Java中的volatile
关键字提供了一个功能,那就是被其修饰的变量在被修改后可以立即同步到主内存,被其修饰的变量在每次是用之前都从主内存刷新。因此,可以使用volatile
来保证多线程操作时变量的可见性。
3:volatile有序性
volatile可以禁止指令重排,这就保证了代码的程序会严格按照代码的先后顺序执行。这就保证了有序性。被volatile
修饰的变量的操作,会严格按照代码顺序执行,load->add->save
的执行顺序就是:load、add、save。
3:volatile与原子性(不能保证的)
线程是CPU调度的基本单位。CPU有时间片的概念,会根据不同的调度算法进行线程调度。当一个线程获得时间片之后开始执行,在时间片耗尽之后,就会失去CPU使用权。所以在多线程场景下,由于时间片在线程间轮换,就会发生原子性问题。
synchronized需要通过字节码指令monitorenter
和monitorexit来保证原子性,vo
latile
和这两个指令之间是没有任何关系的。
所以,volatile
是不能保证原子性的。
4:开销
volatile变量的读、写操作都不会导致上下文切换,因此volatile的开销比锁小。但volatile变量的zh值每次都要从高速缓存或者主内存中读取,无法被暂存在寄存器中,从而无法发挥访问的高效性。
注意:volatile关键字并非锁的替代品,volatile和锁各有其使用条件。volatile更适用于多个线程共享一个状态变量,而后者更适用于多个线程共享一组状态变量。当然在某些情况下,我们可以将多个线程共享的一组状态变量合并成一个对象,用一个volatile变量来引用该对象,从而在该情况下不需要使用锁。