以前只知道Java中,单例模式有两种方法:
/*
* 方法一:
* 没有延迟加载,但却最简单
*/
public class Singleton {
private static final Singleton mInstance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return mInstance;
}
}
方法一,也是《模式设计之禅》中写的,最简单省事的方法。
/*
* 方法二:
* 延迟加载
*/
public class Singleton {
private static Singleton mInstance = null;
private Singleton(){}
public synchronized static Singleton getInstance(){
if(mInstance == null){
mInstance = new Singleton();
}
return mInstance;
}
}
方法二,是为了延迟初始化mInstance而想出来的方法。
注:在方法上加了syncrhonized关键字。
那么,每次访问该方法,都会进入同步区,降低了访问性能,因为mInstance只在第一次为null,之后就已经有值了。不过,现在代的JVM性能已经很高了,所以,这里的性能损失也就可以忽略。
-------------------------------------------------------分割线------------------------------------------------------------
后来,在网上搜索了下JAVA单例还有没有其它方法,果然,另我汗颜,原来一共有5种,除去我上面讲的2种,下面讲其它3种:
/*
* 方法三:
* 静态内部类,加载时没有初始化mInstance,因此达到了延迟加载
*/
public class Singleton {
private static class InternalSingleton{
private static final Singleton mInstance = new Singleton();
}
private Singleton(){}
public static Singleton getInstance(){
return InternalSingleton.mInstance;
}
}
/*
* 方法四:
* 用枚举来实现单例模式
* 《effective java》中作者推荐,也是能避免线程同步,
* 同时还能避免Java中的反射(即反序列化)导致多个实例
*
* 只有JDK1.5之后才支持枚举
*
* 使用方法:Singleton.mInstance.function
*/
public enum Singleton{
mInstance; // 默认mInstance = this
public void function(){
// ......
}
}
/*
* 方法五:(在JAVA内存模型中,线程不安全,知道理论上可行就行了)
* 双检索
* 初衷:对比方法二,多个线程每次去getInstance时,
* 都会进入synchronized状态,哪怕mInstance已经不
* 为null,因此降低了性能,所以,将synchronized
* 放在第一个if判断之后,再判断一次
*
* IBM大神有分析这种情况导致不安全的原因:
* http://www.ibm.com/developerworks/cn/java/j-dcl.html
*/
public class Singleton {
private static Singleton mInstance = null;
private Singleton(){}
public static Singleton getInstance(){
if(mInstance == null){
synchronized(Singleton.class){
if(mInstance == null){
mInstance = new Singleton();
}
}
}
return mInstance;
}
}
本篇一共讲了5种方法,除了第5种“双检索”方法,知道就行了,但用的话,还是使用其它4种方法,不过,这里还是要说明点(网上也可以找到):
只有方法4,不但线程安全,同时也能JAVA反射机制,创造出N个单例的实例,不过我也说过,方法4使用的是枚举型,因为,至少要保证JDK1.5之上才行。
如果不讨论反射机制导致的问题,方法1,2,3和4都是可以用的。
因此,大家使用单例模式时,也请注意!