一、介绍

单例模式是应用最广的模式之一,也可能是很多初级工程师唯一会使用的设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个全局对象,这样有利于我们协调系统整体的行为。

二、定义

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

三、使用场景

确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有一个。例如,创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源,这时就要考虑使用单例模式。

四、实现方式

1、饿汉模式

示例代码:

/*** 饿汉模式*/
public class Singleton {private static Singleton instance;private Singleton(){}public static Singleton getInstance(){if(instance == null){instance = new Singleton();}return instance;}
}

优点:延迟加载(需要的时候才去加载)

缺点:线程不安全,在多线程中很容易出现不同步的情况,如在数据库对象进行的频繁读写操作时。

2、懒汉模式

示例代码:

/*** 懒汉模式*/
public class Singleton {private static Singleton instance;private Singleton(){}public static synchronized Singleton getInstance(){if(instance == null){instance = new Singleton();}return instance;}
}

与饿汉模式相比,getInstance()方法中添加了synchronized关键字,也就是说getInstance是一个同步方法,这就在多线程的情况下保证单例对象的唯一性的手段。但是,细想一下,大家可能会发现一个问题,即使instance已经被初始化(第一次调用的时候就会被初始化instance),每次调用getInstance方法都会进行同步,这样会消耗不必要的资源,这也是懒汉模式存在的最大问题。

优点:解决了线程不安全的问题。

缺点:第一次加载时需要及时进行实例化,反应稍慢,最大问题是每次调用getInstance都进行同步,造成不必要的同步开销。

补充:在Android源码中使用的该单例方法有:InputMethodManager,AccessibilityManager等都是使用这种单例模式

3、Double Check Lock(DCL)双重检查锁定

示例代码:

/*** 双重检查锁定(DCL)单例模式*/
public class Singleton {private static Singleton instance;private Singleton(){}public static Singleton getInstance(){if(instance == null){synchronized (Singleton.class) {if(instance == null) {instance = new Singleton();}}}return instance;}
}

本程序的亮点自然在getInstance方法上,可以看到getInstance方法中对instance进行了两次判空:第一层判断主要是为了避免不必要的同步,第二层的判断则是为了在null的情况下创建实例。

假设线程A执行到instance = new Singleton()语句,这里看起来是一句代码,但实际上它并不是一个原子操作,这句代码最终会被编译成多条汇编指令,它大致做了3件事情:

(1)个Singleton的实例分配内存;

(2)调用Singleton()的构造函数,初始化成员字段;

(3)将instance对象指向分配的内存空间(此时instance就不是null了)。

但是,由于java编译器允许处理器乱序执行,以及JDK1.5之前JMM(Java Memory Model,即Java内存模型)中Cache、寄存器到主内存回写顺序的规定,上面的第二和第三句的顺序是无法保证的。也就是说,执行顺序可能是1-2-3也可能是1-3-2。如果是后者,并且在3执行完毕、2未执行之前,被切换到线程B上,这时候instance因为已经在线程A内执行过了第三点,instance已经是非空了,所有,线程B直接取走了instance,再使用时就会出错,这就是DCL失效问题,而且这种难以跟踪难以重现的错误很可能会隐藏很久。

在JDK1.5之后,SUN官方已经注意到这种问题,调整了JVM,具体化了volatile关键字,因此,如果JDK是1.5或之后的版本,只需要将instance的定义改成private volatile static Singleton instance就可以保证instance对象每次都是从主内存中读取,就可以使用DCL的写法来完成单例模式。当然,volatile或多或少也会影响到性能,但考虑到程序的正确性,牺牲这点性能还是值得的。

优点:资源利用率高,第一次执行getInstance时单例对象才会被实例化,效率高。在并发量不多,安全性不高的情况下或许能很完美运行单例模式

缺点:第一次加载时反应稍慢,也由于Java内存模型的原因偶尔会失败。在高并发环境下也有一定的缺陷,虽然发生概率很小。

补充:在android图像开源项目Android-Universal-Image-Loader (https://github.com/nostra13/Android-Universal-Image-Loader)中使用的是这种方式。

DCL模式是使用最多的单例实现方式,它能够在需要时才实例化单例对象,并且能够在绝大多数场景下保证单例对象的唯一性,除非你的代码在并发场景比较复杂或者低于JDK6版本下使用,否则,这种方式一般能够满足需要。

4、静态内部类单例模式

DCL虽然在一定程度上解决了资源消耗、多余的同步、线程安全等问题,但是,它还是在某些情况下出现失效的问题。这个问题被称为双重检查锁定(DCL)失效,在《Java并发编程实践》一书的最后谈到了这个问题,并指出这种“优化”是丑陋的,不赞成使用。而建议使用如下的代码替代:

示例代码:

/*** 静态内部类单例模式*/
public class Singleton {private Singleton(){}public static Singleton getInstance(){return SingletonHolder.instance;}/*** 静态内部类* 延迟加载,减少内存开销 */private static class SingletonHolder{private static final Singleton instance = new Singleton();}
}

当第一次加载Singleton类时并不会初始化instance,只有在第一次调用Singleton的getInstance方法时才会导致instance被初始化。因此,第一次调用getInstance方法会导致虚拟机加载SingletonHolder类,这种方式不仅能够确保线程安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化,所以这是推荐使用的单例模式实现方式。

优点:延迟加载,线程安全(java中class加载时互斥的),也减少了内存消耗

5、枚举单例

前面讲解了一些单例模式实现方式,但是,这些实现方式不是稍显麻烦就是会在某些情况下出现问题。

示例代码:

/*** 枚举单例模式*/
public enum Singleton {  /** * 1.从Java1.5开始支持; * 2.无偿提供序列化机制; * 3.绝对防止多次实例化,即使在面对复杂的序列化或者反射攻击的时候; */  instance;  private String others;  Singleton() {  }  public String getOthers() {  return others;  }  public void setOthers(String others) {  this.others = others;  }
} 

写法简单是枚举单例最大的优点,枚举在Java中与普通的类是一样的,不仅能够有字段,还能有自己的方法。最重要的是默认枚举实例的创建是线程安全的,并且在任何情况下它都是一个单例。

为什么这么说呢?在上述的几种单例模式实现中,在一个情况下它们会出现重新创建对象的情况,那就是反序列化。

通过序列化可以将一个单例的实例对象写到磁盘,然后在读回来,从而有效的获得一个实例。即使构造函数是私有的,反序列化时依然可以通过特殊的途径去创建类的一个新的实例,相当于调用该类的构造函数。反序列化操作提供了一个特别的钩子函数,类中具有一个私有的、被实例化的方法readResolve(),这个方法可以让开发人员控制对象的反序列化。例如,上述几个示例中如果要杜绝单例对象在被反序列化时重新生成对象,那么必须加入如下方法:

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

也就是在readResolve方法中将instance对象返回,而不是默认的重新生成一个新的对象。而对于枚举,并不存在这个问题,因为即使反序列化它也不会重新生成新的实例。

优点:无偿提供序列化机制,绝对防止多次实例化,即使在面对复杂的序列化或者反射攻击的时候。

缺点:从Java1.5开始支持。

上面主要讲了单例模式5种创建方法,大家可以根据其优缺点进行个人实际项目中的使用。

Android设计模式之——单例模式相关推荐

  1. android 单例模式的使用场景,android设计模式之单例模式

    (其实设计模式应该从属于java,但是会专门针对android做相应的解释,所以就取名为android设计模式~) 一.单例模式的介绍 单例模式是应用最广的模式之一,在应用这个模式的时候,单例对象的类 ...

  2. android中的单例模式,Android中的单例模式

    定义: 单例模式:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 使用场景: 确保某一个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有一 ...

  3. java/android 设计模式学习笔记(1)--- 单例模式

    前段时间公司一些同事在讨论单例模式(我是最渣的一个,都插不上嘴 T__T ),这个模式使用的频率很高,也可能是很多人最熟悉的设计模式,当然单例模式也算是最简单的设计模式之一吧,简单归简单,但是在实际使 ...

  4. Android设计模式——单例模式(Singleton)

    二十三种设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元 ...

  5. java/android 设计模式学习笔记(1)---单例模式

    前段时间公司一些同事在讨论单例模式(我是最渣的一个,都插不上嘴 T__T ),这个模式使用的频率很高,也可能是很多人最熟悉的设计模式,当然单例模式也算是最简单的设计模式之一吧,简单归简单,但是在实际使 ...

  6. java/android 设计模式学习笔记目录

    其实很早就想开始总结设计模式了,无奈刚刚换完工作,工作太忙,平时周末也太懒,难得提起精神写一点,估计时间会花的很长,不过还是自己加油吧~~. 学习笔记,顾名思义,其实就是我在平时看书,工作的笔记而已, ...

  7. java/android 设计模式学习笔记(8)---桥接模式

    这篇博客我们来介绍一下桥接模式(Bridge Pattern),它也是结构型设计模式之一.桥接,顾名思义,就是用来连接两个部分,使得两个部分可以互相通讯或者使用,桥接模式的作用就是为被分离了的抽象部分 ...

  8. java/android 设计模式学习笔记(3)---工厂方法模式

    这篇来介绍一下工厂方法模式(Factory Method Pattern),在实际开发过程中我们都习惯于直接使用 new 关键字用来创建一个对象,可是有时候对象的创造需要一系列的步骤:你可能需要计算或 ...

  9. Android设计模式

    android开发中使用到的一些设计者模式-  http://blog.csdn.net/xiangzhihong8/article/details/28593827 引用: http://m.blo ...

最新文章

  1. apache与tomcat连接
  2. java hashmap 去重复_为什么我在Java HashMap中得到重复的键?
  3. Netweaver和CloudFoundry的log设置
  4. Sonnedix收购意大利11.2MW光伏电站产品组合
  5. python获取pid并杀死_用python记录运行pid,并在需要时kill掉它们的实例
  6. Python中的特殊属性与方法
  7. html5 java 图片上传_java实现图片上传至服务器并显示,如何做?希望要具体的代码实现...
  8. python方法解析顺序_浅谈Python的方法解析顺序(MRO)
  9. SPS技术体系之一-----程序文件和IIS配置
  10. python卸载_技术 | Python 包安装和卸载的几种方式
  11. 使用php语言制作水印
  12. 习题——路由与交换机技术(五)
  13. 物联网平台由哪些部分组成
  14. 转:一个硕士应届生的面试总结.(很精彩,特别是具体的各公司面试信息)
  15. nRF52832之硬件I2C
  16. echarts折线图实现滑动平移,横坐标过多时隐藏部分横坐标,通过滑动平移显示隐藏的部分
  17. 追剧达人观看电影源码
  18. 移植三星的uboot(三)
  19. C#(WPF)实现拳皇(一)
  20. 【转载】VTD_RDB_TrafficLights 数据解析提取

热门文章

  1. 神经网络与深度学习——TensorFlow2.0实战(笔记)(三)(python运算符和表达式)
  2. 【转】为什么博士叫PhD?
  3. 【转】Windows Azure的账户体系
  4. ASP.NET MVC 入门6、TempData
  5. 第十二节:Lambda、linq、SQL的相爱相杀(1)
  6. Execute SQL Task 参数和变量的映射
  7. oracle 存储同步,Oracle数据库知识——存储过程篇
  8. 新手前端练手网站_编程到底难不难学?新手入门选择哪种语言好?
  9. 面向对象软件开发代码结构(1)
  10. java 一元线性回归_一元线性回归的java实现