下面对单例模式的懒汉式与饿汉式进行简单介绍:
- 饿汉式:在程序启动或单件模式类被加载的时候,单件模式实例就已经被创建。
- 懒汉式:当程序第一次访问单件模式实例时才进行创建。
本篇主要讨论使用 懒汉式 创建单例模式的高效做法。
- 写法一:如下代码所示:
public class SingleTon{
private static volatile sInstance;
private SingleTon(){
}
public static SingleTon getInstance(){
if(sInstance == null){
synchronized(SingleTon.class){
if(sInstance == null){
sInstance = new SingleTon();
}
}
}
return sInstance;
}
}
上面的单例写法是最常见的 双重检查锁 的单例写法,但是性能上还可以提高。
- 写法二:如下代码所示:
public class SingleTon{
private static volatile sInstance;
private SingleTon(){}
public static SingleTon getInstance(){
SingleTon inst = sInstance;
if(inst == null){
synchronized(SingleTon.class){
inst = sInstance;
if(inst == null){
inst = new SingleTon();
sInstance = inst;
}
}
}
return inst; //这里返回的是临时变量
}
}
理论分析:
写法二比写法一高效的原因:在 Java 5 之后,引入扩展关键字 volatile 的功能,它能保证:
-
禁止重排序:对 volatile 变量的写操作,不允许和它之前的读写操作打乱顺序;对 volatile 变量的读操作,不允许和它之后的读写乱序。
由于volatile具备上述特性,从而也导致了对volatile变量的读写操作是一个比较重的操作。因此,涉及volatile变量的操作时,如果可以,最好定义一个局部变量指向
其真正的内存对象,后续的操作就是对该局部变量的操作(也就是尽量减少对voaltile声明的变量操作).
法2就是基于以上的原理,相较于法1,性能能提高 * 25% *(Wikipedia)
具体分析:
法一:
假设sInstance未被创建,那么操作volatile变量(即sInstance)的次数是:** 4 次(判读2次+创建1次+返回1次)
假设sInstance已被创建,那么操作volatile变量(即sInstance)的次数是: 2 **次(判断1次+返回1次)
法二:
假设sInstance未被创建,那么操作volatile变量(即sInstance)的次数是:** 3 次(局部变量赋值3次)
假设sInstance已被创建,那么操作volatile变量(即sInstance)的次数是: 1 **次(局部变量赋值1次)
从上面可以看成,法2对volatile的操作次数在所有的情形下都比法1少,因此,法2的单例更加高效。