volatile关键字的特性
1、保证被volatile定义的变量对所有线程的可见性
-
即某线程对volatile变量的操作,其他线程会立刻得知
2、使用volatile变量的语义是禁止指令重排序
-
volatile变量前面的操作结果对于后面的操作可见的。
-
volatile变量前面的代码一定会先于后面的代码执行。
由于普通写法的懒汉式单例模式在多线程情况下是不安全的,所以出现了安全的懒汉式单例模式的写法,即双重检验锁模式。
class Singleton{
// 确保产生的对象完整性
private volatile static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if(instance==null) { // 第一次检查
synchronized (Singleton.class) {
if(instance==null) // 第二次检查
instance = new Singleton();
}
}
return instance;
}
}
双重检验锁的原因:
1、第一次检查,目的是检查当前对象是否已经被初始化。
2、第二次检查,是防止对象被初始化多次。
假设线程A在完成对象的初始化后,释放锁,在返回此对象之前,线程B获得锁,然后再次进行初始化操作。这样会导致生成多个对象实例。
3、使用volatile关键字,是为了保证对象被初始化完成后,才被返回。
因为 instance = new Singleton();不是原子性操作。假设线程A还尚未将对象的属性初始化完毕,而线程B在第一次检查时,就发现当前对象不为空,然后返回该对象。这样就会破坏对象的完整性。