设计模式--单例模式

单例模式

一个类只能产生一个对象。确保某一个只有一个实例,而且自行实例化并向整个系统提供这个实例且构造函数私有化。

单例模式
1
2
3
4
5
6
7
8
9
10
11
12
13
//单例模式通用代码
public class Singleton{
private static final Singleton singleton = new Singleton();
private Singleton(){

}
public static Singleton getSingleton(){
return singleton;
}
public static void doSth(){

}
}

单例模式优点

  1. 由于单例模式只存在一个实例,减少了内存开支
  2. 减少了系统的开销
  3. 避免对资源的多重占用
  4. 在系统设置全局访问点,优化和共享资源访问
  5. 允许可变数目的实例,基于单例模式可以进行拓展,可以获得指定个数的对象实例,既节省系统资源,又解决单例对象过多的性能损耗

单例模式缺点

  1. 单例模式一般没有接口,扩展很困难。
  2. 单例模式对测试是不利的,因为单例模式没有完成时,是不能进行测试的。
  3. 与单一职责原则有冲突

单例模式使用场景

  1. 要求生成唯一序列号的环境
  2. 在整个项目中需要一个共享访问点或共享数据
  3. 创建一个对象需要的消耗资源过多,如访问IO和数据库
  4. 需要定义大量的静态常亮和静态方法

单例模式的实现条件

  1. 构造函数私有
  2. 有一个该类的静态私有对象
  3. 有一个静态的公用函数用于创建或获取本身静态私有对象
  4. 线程同步

单例模式注意事项

  1. 在高并发情况下,注意单例模式的线程同步问题
  2. 考虑对象的复制情况

单例模式实现实例

  • 双重校验锁(DCL)–Java5之后可以安心使用该方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class Singleton {
    private static volatile Singleton mInstance;
    private Singleton() {
    }

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

    //反序列化提供的一个特殊方法,可以控制对象的反序列化
    private Object readResolve(){
    return mInstance;//返回实例对象
    }
    }

    在JVM中,并不限制处理器的执行顺序,在不影响运行结果的情况下,顺序可能会被打乱。volatile 关键字的作用是防止执行命令不会被重新排序。如若没有volatile 修饰,则mInstance = new Singleton();可能出现mInstance尚未被初始化的异常。

    mInstance = new Singleton();包括三步:为对象分配内存执行构造方法,初始化实例对象把mInstance的引用指向分配的内存空间。在JVM的执行过程中并不是原子性的。

  • 静态内部类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Singleton{
    private static class SingletonHolder{
    private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton(){

    }
    public static Singleton getInstance(){
    return SingletonHolder.INSTANCE;
    }
    }

    为何可以保证线程安全?虚拟机可以保证一个类的类构造器()`在多线程环境中被正确的加锁、同步。如果多个线程同时去初始化一个类,只有一个线程可以去执行类构造方法,其他线程都会阻塞等待,直到构造方法执行完毕。同一个类加载器下,一个类型只会被初始化一次。

  • 枚举

    1
    2
    3
    public enum Singleton {
    INSTANCE;
    }

    防止反序列化重新创建新对象,在Android中不建议使用枚举,大概会增加4倍消耗。

单例模式在Android中的体现

1
2
3
4
5
6
7
8
9
10
11
12
13
//../android/view/inoputmethod/InputMethodManager.java 
public static InputMethodManager getInstance() {
synchronized (InputMethodManager.class) {
if (sInstance == null) {
try {
sInstance = new InputMethodManager(Looper.getMainLooper());
} catch (ServiceNotFoundException e) {
throw new IllegalStateException(e);
}
}
return sInstance;
}
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!