美文网首页Java设计模式设计模式
《设计模式》单例模式

《设计模式》单例模式

作者: 敏捷Studio | 来源:发表于2019-08-08 00:33 被阅读0次

定义

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

介绍

单例模式属于创建类模式。有如下特点:

  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其他对象提供这一实例。

实现

\color{red}{饿汉式(线程安全):}

public class Singleton {
  // 在类初始化时,已经自行实例化,所以是线程安全的
  private static final Singleton singleton = new Singleton(); 

  // 构造方法为private,防止外部代码直接通过new来构造多个对象
  private Singleton() {
  }

  // 通过getInstance()方法获取实例对象
  public static Singleton getInstance() {
    return singleton;
  }
}  

优点:写法简单,线程安全。
缺点:没有懒加载的效果,如果没有使用过的话会造成内存浪费。

\color{red}{懒汉式(线程不安全):}

public class Singleton {
  private static Singleton singleton = null;

  private Singleton() {
  }

  public static Singleton getInstance() {
    if (singleton == null) {
      // 在第一次调用getInstance()时才实例化,实现懒加载,所以叫懒汉式
      singleton = new Singleton(); 
    }
    return singleton;
  }
} 

优点:实现了懒加载的效果。
缺点:线程不安全。

\color{red}{懒汉式(线程安全):}

public class Singleton {
  private static Singleton singleton = null;

  private Singleton() {
  }
  
  // 加上synchronized同步
  public static synchronized Singleton getInstance() {
    if (singleton == null) {
        singleton = new Singleton();
    }
    return singleton;
  }
}  

优点:实现了懒加载的效果,线程安全。
缺点:使用synchronized会造成不必要的同步开销,而且大部分时候我们是用不到同步的。

\color{red}{双重检查锁定(DCL):}

public class Singleton {
  // volatile 能够防止代码的重排序,保证得到的对象是初始化过
  private volatile static Singleton singleton;

  private Singleton() {
  }

  public static Singleton getSingleton() {
    // 第一次检查,避免不必要的同步
    if (singleton == null) {
      // 同步
      synchronized (Singleton.class) {
        // 第二次检查,为null时才创建实例
        if (singleton == null) {
          singleton = new Singleton();
        }
      }
    }
    return singleton;
  }
} 

优点:懒加载,线程安全,效率较高
缺点:volatile影响一点性能,高并发下有一定的缺陷,某些情况下DCL会失效,虽然概率较小。

\color{red}{静态内部类(推荐使用):}

public class Singleton {
  private Singleton() {
  }

  public static Singleton getInstance() {
    // 第一次调用getInstance方法时才加载SingletonHolder并初始化sInstance
    return SingletonHolder.sInstance;
  }

  // 静态内部类
  private static class SingletonHolder {
    private static final Singleton sInstance = new Singleton();
  }
}

优点:懒加载,线程安全,推荐使用

\color{red}{枚举单例:}

public enum Singleton {
  // 定义一个枚举的元素,它就是Singleton的一个实例
  INSTANCE;
  
  public void doSomething() {
  }
}  

优点:线程安全,写法简单,能防止反序列化重新创建新的对象。
缺点:可读性不高,枚举会比静态常量多那么一丁点的内存。

\color{red}{使用容器实现单例模式:}

// 单例管理类
public class SingletonManager {
  private static Map<String, Object> objMap = new HashMap<String, Object>();

  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);
  }
}

优点:方便管理。
缺点:写法稍复杂。

注意事项

使用反射能够破坏单例模式,所以应该慎用反射

Constructor con = Singleton.class.getDeclaredConstructor();
con.setAccessible(true);
// 通过反射获取实例
Singleton singeton1 = (Singleton) con.newInstance();
Singleton singeton2 = (Singleton) con.newInstance();
// 结果为false,singeton1和singeton2将是两个不同的实例
System.out.println(singeton1 == singeton2);

可以通过当第二次调用构造函数时抛出异常来防止反射破坏单例,以懒汉式为例:

public class Singleton {
  private static boolean flag = true;
  private static Singleton singleton = null;

  private Singleton() {
    if (flag) {
      flag = !flag;
    } else {
      throw new RuntimeException("单例模式被破坏!");
    }
  }

  public static Singleton getInstance() {
    if (singleton == null) {
      singleton = new Singleton();
    }
    return singleton;
  }
}  

反序列化时也会破坏单例模式,可以通过重写readResolve方法避免,以饿汉式为例:

public class Singleton implements Serializable {
  private static final Singleton singleton = new Singleton();

  private Singleton() {
  }

  public static Singleton getInstance() {
    return singleton;
  }

  // 重写readResolve()
  private Object readResolve() throws ObjectStreamException {
    // 直接返回单例对象
    return singleton;
  }
} 

应用场景

  • 频繁访问数据库或文件的对象。
  • 工具类对象。
  • 创建对象时耗时过多或耗费资源过多,但又经常用到的对象。

优缺点

优点:

  • 内存中只存在一个对象,节省了系统资源。
  • 避免对资源的多重占用,例如一个文件操作,由于只有一个实例存在内存中,避免对同一资源文件的同时操作。

缺点

  • 获取对象时不能用new
  • 单例对象如果持有Context,那么很容易引发内存泄露。
  • 单例模式一般没有接口,扩展很困难,若要扩展,只能修改代码来实现。

相关文章

  • 单例模式Java篇

    单例设计模式- 饿汉式 单例设计模式 - 懒汉式 单例设计模式 - 懒汉式 - 多线程并发 单例设计模式 - 懒汉...

  • python中OOP的单例

    目录 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计模式 是 前人...

  • 单例

    目标 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计模式 是 前人...

  • 设计模式 - 单例模式

    设计模式 - 单例模式 什么是单例模式 单例模式属于创建型模式,是设计模式中比较简单的模式。在单例模式中,单一的类...

  • 设计模式

    常用的设计模式有,单例设计模式、观察者设计模式、工厂设计模式、装饰设计模式、代理设计模式,模板设计模式等等。 单例...

  • 2018-04-08php实战设计模式

    一、单例模式 单例模式是最经典的设计模式之一,到底什么是单例?单例模式适用场景是什么?单例模式如何设计?php中单...

  • python 单例

    仅用学习参考 目标 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计...

  • 基础设计模式:单例模式+工厂模式+注册树模式

    基础设计模式:单例模式+工厂模式+注册树模式 单例模式: 通过提供自身共享实例的访问,单例设计模式用于限制特定对象...

  • 单例模式

    JAVA设计模式之单例模式 十种常用的设计模式 概念: java中单例模式是一种常见的设计模式,单例模式的写法...

  • 设计模式之单例模式

    单例设计模式全解析 在学习设计模式时,单例设计模式应该是学习的第一个设计模式,单例设计模式也是“公认”最简单的设计...

网友评论

    本文标题:《设计模式》单例模式

    本文链接:https://www.haomeiwen.com/subject/vgwzdctx.html