狐彻设计模式学习笔记(一)—— 单例模式(Singleton)

设计模式(一)—— 单例模式(Singleton)

基于《Android 源码设计模式 解析与实战》第二版(何红辉 关爱民 著)的学习笔记

定义

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

使用场景

  • 需确保某个类有且只有一个对象的场景
  • 需避免产生多个对象消耗过多资源
  • 某类型对象应有且只有一个实例

如创建一个对象需要消耗的资源过多(IO、网络交互、数据库)

角色

  • Client —— 高层客户端
  • Singleton —— 单例类

特点

  • 构造函数不对外开放,一般为private
  • 通过一个静态方法或枚举类返回单例对象
  • 确保单例类的对象有且只有一个,尤其是并发环境下
  • 确保单例对象在反序列化时不会重新构建对象

实现方式

1. 饿汉模式模式

在生命静态对象时就已经初始化。因急于初始化,被称为饿汉模式

class Singleton {
    private static final Singleton instance = new Singleton();
    
    private Singleton(){}
    
    static Singleton getInstance(){
        return instance;
    }
}

2. 懒汉模式

生命一个静态对象,并在用户第一次调用静态获取方法时进行初始化,用sychronized关键字将获取方法标记为同步方法。

class Singleton {
    private static final Singleton instance = null;
    
    private Singleton(){}
    
    static synchronized Singleton getInstance(){
        if (instance == null) 
            instance = new Singleton();
        return instance;
    }
}
  • 优点:只有在使用时才会被实例化,一定程度的节省资源
  • 缺点:即使instance已被初始化,每次调用getInstance都会进入同步,造成不必要的资源消耗

3. 双重锁模式、Double Check Lock (DCL)

getInstance方法中对instance进行两次判空
第一次判空只要为避免不必要同步,第二次的判断则为了在未实例化的情况下实现实例化。

class Singleton {
    private static final Singleton instance = null;
    
    private Singleton(){}
    
    static Singleton getInstance(){
        if (instance == null) {
            synchronized(Singleton.class) {
                if(instance == null) 
                    instance = new Singleton();
                return instance;
            }
        }
           
        return instance;
    }
}
  • 优点:
    • 资源库利用率高,既能够在需要时才初始化单例
    • 保证线程安全,且对象初始化后不再进行同步锁
  • 缺点:
    • 第一次加载反应稍慢
    • 高并发下可能发生DCL失效

DCL失效:
在调用到Singleton()而在初始化instance前跳转到其他线程,并在该线程完成instance的赋值。此时切换回原线程,则会继续执行instance的初始化。从而导致出错。可以用volatile关键字解决此问题,不过该关键字也会影响性能。

静态内部类

为解决双重所失效(DCL失效)问题,可以用静态内部类的特性实现单例
只有在调用到静态内部类时,该静态内部类才会被虚拟机加载。

class Singleton {
    private Singleton(){}
    
    static Singleton getInstance(){
        return SingletonHolder.instance;
    }
    
    static class SingletonHolder {
        private static final Singleton instance = new Singleton();
    }
}
  • 第一次加载Singleton类时不会初始化instance
  • 第一次调用SingletongetInstance方法时才会导致sInstance被初始化
  • 保证线程安全
  • 保证单例对象的唯一性
  • 延迟了单例的实例化
  • 是推荐使用的单例模式实现方式

枚举单例

通过枚举类特性实现单例

enum SingletonEnum {
    INSTANCE;
    
    void doSomething() {
        System.out.printlin("do sth.")
    }
}
  • 枚举类在Java中与普通类是一样的,能拥有字段和自己的方法
  • 默认是线程安全的
  • 任何情况下都是一个单例

容器单例

容器形式的单例

class SingletonManager { 
    private static Map<String, Object> objMap = new HashMap<>();
    
    private SingletonManager(){}
    
    static void registerService(String key, Object instance) {
        if(!objMap.containsKey(key)) 
            objMap.put(key, instance);
    }
    
    static Object getService(String key) {
        return objMap.get(key);
    }
}

代码很清晰,而且是不是看起来很像ContextgetService

小结

  • 优点:
    • 减少内存开支
    • 减少性能开销
    • 避免资源多重占用
    • 共享资源访问
  • 缺点:
    • 一般没有接口,拓展困难。若要拓展基本上只能修改代码
    • 若单例持有Context,则可能造成内存泄漏。最好传递Application Content
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值