Android开发经验谈Android技术知识程序员

设计模式——单例模式

2019-03-26  本文已影响34人  AndroidDMW

1.单例模式介绍

单例模式是应用最广的模式,也是我最先知道的一种设计模式,在深入了解单例模式之前,每当遇到如getInstance()这样的创建实例的代码时,我都会把它当做一种单例模式的实现。
在应用这个模式时,单例对象的类必须保证只有一个实例存在。很多时候系统只需要一个全局对象来协调系统整体的行为,比如在应用中只有一个ImageLoader实例,因为它含有线程池、缓存系统、网络请求等。
像这种不能自由构造对象的情形就是单例模式的使用场景。

2.单例模式定义

保证一个类仅有一个实例,并且自行实例化向整个系统提供这个实例

3.单例模式使用场景

确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象有且只有一个。
例如:创建一个对象需要消耗的资源过多(访问IO和数据库等)

4.单例模式实现关键点

通过将单例类的构造函数私有化,使得客户端代码不能通过new的形式手动构造单例类的对象。单例类会暴露一个公有静态方法,客户端需要调用这个静态方法获取到单例类的唯一对象,在获取这个单例对象的过程中需要确保线程安全,即在多线程环境下构造单例类的对象也是有且只有一个,这也是单例模式实现中的难点。

5.实现方式

5.1懒汉式
public class Singleton { 

    private static Singleton sInstance ; 
    private Singleton (){};

    public static synchronized Singleton getInstance() { 
        if (instance == null) { 
            sInstance = new Singleton(); 
        } 
        return sInstance ; 
    } 
} 
5.2饿汉式
public class Singleton { 
    //static修饰的静态变量在内存中一旦创建,便永久存在
    private static Singleton sInstance = new Singleton(); 
    private Singleton (){} 
    public static Singleton getInstance() { 
        return sInstance ; 
    } 
}
static { 
    sInstance = new Singleton(); 
}
5.3 Double Check Lock(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();
                }
            }
        }
        return sInstance ;
    }
}

但是由于Java编译器允许处理器乱序执行,以及在jdk1.5之前,JMM(Java Memory Model:java内存模型)中Cache、寄存器、到主内存的回写顺序规定,上面的步骤2 步骤3的执行顺序是不保证了。也就是说执行顺序可能是1-2-3,也可能是1-3-2,如果是后者的指向顺序,并且恰恰在3执行完毕,2尚未执行时,被切换到线程B中,这时候因为sInstance 在线程A中执行了步骤3了,已经非空了,所以,线程B直接就取走了sInstance ,再使用时就会出错。这就是DCL失效问题。这种难以跟踪难以重现的错误可能会隐藏很久。
但是在JDK1.5之后,官方给出了volatile关键字,将sInstance 定义的代码改成:

private volatile static Singleton sInstance ; //使用volatile 关键字
5.4 静态内部类单例模式
public class Singleton {
    private Singleton (){} ;
    public static final Singleton getInstance() {
        return SingletonHolder.sInstance ;
    }
    /**
    * 静态内部类
    */
    private static class SingletonHolder {
        private static final Singleton sInstance = new Singleton();  
    }
}
5.5 枚举单例
public enum Singleton {  //enum枚举类
    INSTANCE; 
    public void whateverMethod() { 
          // TODO sth
    } 
}
private Object readResolve()  throws ObjectStreamException{
    return sInstance ;
}
5.6 使用容器实现单例模式
public class SingletonManager {
  private static Map<String, Object> objMap = new HashMap<String,Object>();
    private Singleton() {}
  public static void registerService(String key, Object instance) {
    if (!objMap.containsKey(key) ) {
      objMap.put(key, instance) ;//第一次是存入Map
    }
  }
  public static ObjectgetService(String key) {
    return objMap.get(key) ;//返回与key相对应的对象
  }
}

6.单例模式总结

不管以哪种形式实现单例模式,它们的核心原理是将构造函数私有化,并且通过静态公有方法获取一个唯一的实例,在这个获取的过程中必须保证线程的安全,同时也要防止反序列化导致重新生成实例对象。
选择哪种实现方式取决于项目本身,是否是复杂的并发环境、JDK版本是否过低、单例对象的资源消耗等。

上一篇下一篇

猜你喜欢

热点阅读