volatile具备的两种特性:
1、可见性
2、禁止指令重排
解析:
1、可见性:用volatile修饰的变量,会使线程在使用该变量时,每次都从主内存中读取到工作内存,再使用。
对比普通的变量,线程在使用时,如果工作内存中没有该变量,则先从主内存中拷贝,在使用。如果工作内存中已经存在了该变量,则不再从主内存中重新读取。
volatile变量,由于每次都从主内存中先同步,再使用。由此来保证可见性。
2、禁止指令重排:用volatile修饰的变量,会使线程在使用变量的时候,禁止指令重排。
对比普通的变量,线程在使用时,如果存在两处赋值,而这两处赋值对于后续的计算没有影响,那么可能由于优化的原因,而进行指令重拍。
举例:单例模式(双检锁实现)
public class Singleton{
private static Singleton instance;//定义一个单例
public static Singleton getInstance(){
if ( instance == null ) {
synchronized(Singleton.class){
if( instance == null ){
instance = new Singleton();
}
}
}
}
private Singleton(){} //私有构造方法
}
指令重排,在 instance = new Singleton();这一句中,有可能线程A还未完成全部的初始化工作,就已经将结果赋值给了instance这个变量。
此时:线程B执行第一个if ( instance == null ) 判断,获取到对于的instance实例,在执行该实例对于的方法时,会由于实例未完成初始化工作,而导致异常。
因此:需要将instance变量,定义为volatile类型。
变量可见性的保证,还有另外两种方式:synchronized,final。
synchronized:加锁后,一次只能有一个线程访问变量,而且每次放完变量后,需要unlock变量,变量才能被其它线程使用,因此能保证可见性。
final:final的不变性,保证可见性。
synchronized 与 volatile 的区别是,volatile不保证原子性,所以不是线程安全的。