今天继续上次,继续说创建型设计模式之单例模式.
1.饿汉模式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
第二种写法
public class Singleton {
private Singleton instance = null;
static {
instance = new Singleton();
}
private Singleton (){}
public static Singleton getInstance() {
return this.instance;
}
}
类中用静态成员变量直接实例化对象.
优点:多线程安全,加载class字节码时直接实例化了(这种方式基于classloder机制避免了多线程的同步问题).
缺点:加载慢,效率很低,没有做到随用随加载.
2.懒汉模式
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优点:可以做到随用随加载
缺点:多线程不安全
线程安全的懒汉模式
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
虽然说多线程安全了,但是效率极低,99%的情况下是不需要同步的.
3.DCL(双重校验锁)模式
一般大家多会采用这种写法.
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
这样我们将上锁的粒度降低到了仅仅是初始化实例的那部分,从而使代码即正确又保证了执行效率。这就是所谓的“双检锁”机制(顾名思义)。
注意:这里由于JVM加载类过程,也不是绝对安全的.(JVM加载类过程这里就不多讲了,我也不太懂,感兴趣的朋友可以自己去看看),不过一般不是特别高并发的情况是不会有问题的,想要做到绝对安全,就在静态成员变量前加上volatile关键字.关于volatile关键字的使用,可见博文《volatile关键字》.
4.静态内部类模式
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
这种方式类似以饿汉模式,同样是利用classloder机制来保证初始化instance时只有一个线程,它跟饿汉模式不同的是(很细微的差别):饿汉模式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比饿汉模式就显得很合理.
5.枚举模式
前几个单例模式都存在一个问题,就是在反序列化的时候,会存在实例化2次或多次的问题,于是Effective Java作者Josh Bloch 提倡的这种枚举单例模式.它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊.
public enum MaYun {
himself; //定义一个枚举的元素,就代表MaYun的一个实例
private String anotherField;
MaYun() {
//MaYun诞生要做的事情
//这个方法也可以去掉。将构造时候需要做的事情放在instance赋值的时候:
/** himself =MaYun() {
* //MaYun诞生要做的事情
* }
**/
}
public void splitAlipay() {
System.out.println(“Alipay是我的啦!看你丫Yahoo绿眉绿眼的望着。。。”);
}
}
Call:MaYun.himself.splitAlipay();
(枚举:
枚举中的属性必须放在最前面 ;
枚举中可以和java类一样定义方法;
枚举中的构造方法必须是私有的.)
总结:android中使用饿汉模式较多,好理解线程安全.如果非要实现lazy loading使用内部类模式较多,jdk5后不存在双重校验模式了,所以使用DCL模式也是不错的选择.只有在涉及到反序列化创建对象时,我会试着使用枚举的方式来实现单例.只是个人看法!
后续更新其他模式……