定义

保证一个类仅有一个实例,并提供一个全局访问点

为什么要使用单例模式

当您想控制实例数目,节省系统资源的时候。解决了一个全局使用的类频繁地创建与销毁的问题。
这里引出了一个类的加载时机,什么时候会初始化该类。

饿汉式

声明静态成员变量时就进行初始化。

优点

线程安全。推荐使用

缺点

无法延迟加载,会造成资源浪费。

代码示例

  1. public class SingleObject {
  2. //创建 SingleObject 的一个对象
  3. private static SingleObject instance = new SingleObject();
  4. //让构造函数为 private,这样该类就不会被实例化
  5. private SingleObject(){}
  6. //获取唯一可用的对象
  7. public static SingleObject getInstance(){
  8. return instance;
  9. }
  10. public void showMessage(){
  11. System.out.println("Hello World!");
  12. }
  13. }

懒汉式

懒汉式(也叫延迟加载):声明静态成员变量时就不进行初始化,开始使用的时候才初始化,该模式是面试重灾区,因为该种方式去实现单例,会存在线程安全问题。需要小心处理。

优点

第一次调用才初始化,避免内存浪费。

缺点

使用不当可能引起线程安全问题。

代码示例1(线程不安全)

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

代码示例2(线程安全)

该种方法必须加锁 synchronized 才能保证单例,但加锁会影响效率。

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

代码示例3(双检锁/双重校验锁)

这种方式采用双锁机制,安全且在多线程情况下能保持高性能。

  1. public class Singleton {
  2. private volatile static Singleton singleton;
  3. private Singleton (){}
  4. public static Singleton getSingleton() {
  5. if (singleton == null) {
  6. synchronized (Singleton.class) {
  7. if (singleton == null) {
  8. singleton = new Singleton();
  9. }
  10. }
  11. }
  12. return singleton;
  13. }
  14. }

代码示例4 (登记式/静态内部类)

这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。

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

代码示例5(枚举)

这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
枚举类不能通过 reflection attack 来调用私有构造方法,所以可以防止反序列化。
严格意义上最完美的单例实现方式。

  1. public enum EnumInstance {
  2. INSTANCE;
  3. public void doSomething() {
  4. System.out.println("doSomething");
  5. }
  6. }

框架案例

spring大多数对象都是单例存在的。一般被spring管理的bean,都是业务对象,不是数据对象。
数据源等信息。