浅谈Android移动架构(二)创建模式之单例模式

今天继续上次,继续说创建型设计模式之单例模式.

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模式也是不错的选择.只有在涉及到反序列化创建对象时,我会试着使用枚举的方式来实现单例.只是个人看法!

后续更新其他模式……

    原文作者:简飘飘
    原文地址: https://www.jianshu.com/p/e27ff42ae0b8
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞