以前只知道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都是可以用的。
因此,大家使用单例模式时,也请注意!