盒子
盒子
文章目录
  1. 1. 单例模式的定义
  2. 2. 单例模式的实现
    1. 2.1饿汉模式
      1. 代码示例
      2. 优缺点
    2. 2.2 懒汉模式
      1. 代码示例
      2. 优缺点
    3. 2.3 Double Check Lock
      1. 代码示例
      2. 优缺点
    4. 2.4 静态内部类单例模式
      1. 代码示例
      2. 优缺点
    5. 2.5 枚举单例
      1. 代码示例
      2. 优缺点
    6. 2.6 使用容器实现单例
      1. 代码示例
      2. 优缺点

Android 设计模式:(二)单例模式

前言
本文是对《Adroid 源码设计模式解析与实战》 何红辉、关爱民 著 人民邮电出版社所做的读书笔记。文章是对本书的一些列学习笔记,如若有侵犯到作者权益,还望作者能联系我,我会及时下架。
这本书不错,有兴趣的同学可以买原书看看。
感兴趣的朋友欢迎加入学习小组QQ群: 193765960

版权归作者所有,如有转发,请注明文章出处:https://xiaodanchen.github.io/archives/

1. 单例模式的定义

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

2. 单例模式的实现

在正式介绍单例模式之前,我们有必要了解一下 instance = new Singleton()这样一条语句。乍一看这是一天简单的语句,用来生成一个Singleton类的实例,但实际上他不是一条原子操作。这条代码最终会被编译成多条汇编指令,他大致做了3件事情:
(1)给Singleton实例分配内存。
(2)调用Singleton()构造方法,初始化成员字段。
(3)将instance对象指向分配的内存空间(此时instance就不是null了)。
另外,JVM执行上面三条汇编指令的顺序是不定的,有可能是1-2-3,也有可能是1-3-2。

2.1饿汉模式

代码示例

1
2
3
4
5
6
7
8
public class Singleton{
private static final Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}

优缺点

优点:

  • 线程安全,无需关注多线程的问题。
  • 加载没有延时:在类创建的同时,就已经创建好类的实例
  • 写法简单

缺点:

  • 由于在类创建的时候,就创建了实例(不管你会不会用到),那么就有可能会缓存很多用户根本用不到的实例。
  • 如果类中的实例会消耗较大量的内存,则要慎用饿汉模式创建单例。

2.2 懒汉模式

代码示例

1
2
3
4
5
6
7
8
9
10
11
public class Singleton{
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(null == instance){
intance = new Singleton();
}
return instance;
}
}

优缺点

优点:

  • 在使用时才会创建实例,在一定程度上节省了资源。
    缺点:
  • 第一次加载时需要实例化,反应稍慢。
  • 加了同步锁,造成不必要的同步开销。
  • 如果忘记加同步锁,则是线程不安全的。

2.3 Double Check Lock

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Singleton{
/**
* volatile:
* 告诉JVM不要对volitile所修饰的变量进行拷贝。
* 这样就保证了instance不会在线程内存中保存拷贝,instance每次都从主内存中读取。
*
*/
private volatile static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(null == instance){
synchornized(Singleton.class){
if(null == instance){
intance = new Singleton();
}
}
}
return instance;
}
}

优缺点

优点:

  • 资源利用率高,只有在第一次调用时才会分配资源。
  • 相较于懒汉模式,加载速度更快。
  • 在一定程度上避免了多余的同步,减轻了线程安全的问题。

缺点:

  • 第一次加载时存在延时,反应较慢。
  • 存在失效的情况(虽然概率较小):正如上文所说,如果当前线程调用new Singleton()对应的汇编的执行顺序是1-3-2,当有新的线程调用getinstance()时,instance没有初始化却非空,就造成了instance失效。

2.4 静态内部类单例模式

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton{
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
/**
* 静态内部类
*/
private static class SinletonHolder{
private static final Singleton instace = new Singleton();
}
}

优缺点

第一次加载Singleton类时并不会初始化instance,只有在第一次调用Singleton的getInstace()方法时才会导致instance初始化。因为,第一次调用getInstace()时,JVM才会加载SingletonHolder类。
这种方式不仅能够确保线程的安全,而且延迟了实例化,避免了饿汉模式的资源浪费,同时也能够保证单例对象的唯一性。

2.5 枚举单例

代码示例

1
2
3
4
5
6
public enum SingletonEnum{
INSTANCE;
public void doSomething(){
...
}
}

优缺点

  • 写法简单
  • 线程安全
  • 在任何情况下(即使反序列化),他都是一个实例。2.1到2.4介绍的几种方法,在反序列化(将实例对象写入磁盘再读取出来)时无法保证实例的唯一性。因为反序列化时系统会通过特殊的途径创建一个类的新实例,相当于调用了该类的构造函数,哪怕构造函数是私有的。反序列化操作提供了一个特别的钩子函数readResolve(),这个方法可以让开发人员控制对象的发序列化。例如,上述几个实现单例模式的方法要想杜绝反序列化时重新生成对象,那么必须加入如下方法:
    1
    2
    3
    private Object readResolve() throw ObjectStreamException{
    return instance;
    }

2.6 使用容器实现单例

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class SingletonManager{
private static Map<String,Object> objMap = new HashMap<String,Object>();
private SingletonManager(){}
public static void registerService(String key, Object instance){
if(!objMap.containsKey(key)){
objMap.put(key,instance);
}
}
public static Object getService(String key){
return objMap.get(key);
}
}

优缺点

在程序的初始,将多中单例类型注入到一个统一的管理类中,在使用时根据key获取对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。
例如系统服务,就是这种模式:getSystemService(System.xx_SERVICE)

扫描加群
好好学习,天天向上!