android源码设计模式解析与实战 读书笔记 2 单例模式(上)

单例模式:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

实现单例模式主要的几个关键点

  1. 构造函数不对外开放,一般为private;
  2. 通过一个静态方法或者枚举返回单例类对象;
  3. 确保单例类的对象有且只有一个,尤其是在多线程环境下;
  4. 确保单例类的对象在饭序列化时不会重新构建对象。

实现方式
1. 懒汉式:

public class Singleton{
           private static Singleton instance;
           private Singleton(){}

public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}}
return instance;
     }

2.饿汉式

public class CEO{
private static final CEO mCeo = new CEO();

private CEO(){}

public static CEO getCeo(){
    return mCeo;
}
}

3.Double CheckLock(DCL) 式

public class Singleton{
private static Singleton sInstance = null;

private Singleton(){}

public static Singleton getInstance(){
    if(sInstance == null){
        synchronized(Singleton.class){
            if(sInstance == null){
            sInstance = new Singleton();
            }
        }
    }
}
}

分析:线程执行sInstance = new Singleton()时;这行代码非原子操作,将会被编译为多条汇编指令,大致有:

  • 给Singleton的实例分配内存
  • 调用Singleton()的构造函数,初始化成员字段
  • 将Singleton对象指向分配的内存空间(此时sInstance就不是null了)。

    但是JAVA编译器允许处理器乱序执行,以及JDK1.5之前JMM(Java Memory Model,即Java内存模型)中的Cache,寄存器到主内存会写顺序的规定,上面的第二和第三的顺序是无法保证的,若先执行3在切换回其他线程,将会造成其他线程DCL实效。
    解决方案:将sInstance 添加volatile修饰,保证每次都是从主内存中读取。

DCL优点:资源利用高,效率高。
DCL缺点:第一次加载时稍慢,由于Java内存模型的原因偶尔会失败,在高并发环境下有一定的缺陷。

4.静态内部类式

public class Singleton{
private Singleton(){}
public static Singleton getInstance(){
    return SingletonHolder.sInstance;
}

// 静态内部类
private static class SingletonHolder{
private static final Singleton sInstance =new Singleton();
}
}

分析:第一次加载Singleton类时不会初始化sInstance,只有第一次调用Singleton的getInstance方法时才会导致sInstance被初始化,并且虚拟机加载SingletonHolder。
优点:确保线程安全,保证单例对象的唯一性,延迟单例的实例化。

5.枚举式

public enum SingletonEnum{
    INSTANCE;
}

优点:写法简单,默认线程安全,任何情况下都是单例,包括反序列化情况下。上述方法杜绝反序列重新生成对象,需加入如下方法:

private Object readResolve() throws ObjectStreamException{
    return sInstance;
}

6.使用容器实现

public class SingletonManager{
private static Map objMap = new HashMap();

private Singleton(){}
public static void registerService(String key,Object instance){
    if(!objMap.containsKey(key)){
        objMap.put(key,instance);
    }
}

public static ObjectgetService(String key){
    return objMap.get(key);
}
}

优点:可以管理多种类型的单例,通过统一的接口进行获取操作,掩藏具体实现,降低藕合度。

无论选择哪一种必须保证线程安全,防止反序列化。选择哪种根据复杂的并发环境,JDK版本,单例对象的资源消耗。

你可能感兴趣的:(读书笔记,android,读书笔记,设计模式)