对单例模式简单的总结

137 阅读4分钟

什么是单例模式

单例模式是23种设计模式中的一种,属于创建型模式,这里简单说一下什么是创建型模式

创建型模式用于描述『怎样创建对象』,它的主要特点是将对象的创建与使用分离。包含单例、原型、工厂方 法、抽象工厂、建造者 5 种创建型模式。

单例模式的优点的优缺点

优点

  • 对于频繁使用的对象,可以节省内存,加快对象访问速度,可以省略 new 操作花费的时间。
  • 由于 new 操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻 GC 压力,缩短 GC 停 顿时间。

饿汉式的优缺点

  • 优点:实现简单,不需要加锁,直接在类加载的时候被创建,执行效率高。
  • 线程安全:由于在类加载时就创建单例对象,因此不存在多线程环境下的同步问题。
  • 缺点:类加载时就初始化,浪费内存(即使这个单例没有用到也会被创建)。饿汉模式在类加载的时候就对实例进行创建,实例在整个程序周期都存在。
  • 应用场景:适合单例占用内存比较小,在初始化时就会被用到的情况。如果单例占用的内存比较大,或单例只是在某个特定场景下才会用到,使用饿汉模式就不合适了,这时候就需要用到懒汉模式进行延迟加载。

懒汉式的优缺点

  • 优点:单例是在需要的时候才去创建的,如果单例已经创建,再次调用获取接口将不会重新创建新的对象,而是直接返回之前创建的对象,避免内存浪费。
  • 缺点:相较于饿汉式更复杂一点,首次创建时可能需要加锁。
  • 应用场景:如果某个单例使用的次数少,并且创建单例消耗的资源较多,那么就需要实现单例的按需创建,这个时候使用懒汉模式就是一个不错的选择。

饿汉式单例模式

public class HungryManStyle implements Serializable {  
  
/**  
* 静态变量(单例对象),类加载时就初始化对象(不存在线程安全问题)  
*/  
private static final HungryManStyle INSTANCE = new HungryManStyle();  
  
private HungryManStyle() {  
//保证反射不能创建对象  
if (INSTANCE != null) {  
throw new RuntimeException("单例对象不能重复创建");  
}  
}  
  
public HungryManStyle getInstance() {  
return INSTANCE;  
}  
  
public Object readResolve(){  
return INSTANCE;  
}  
}

枚举饿汉式单例模式

public enum EnumerateHungryStyle {  
INSTANCE;  
public void method(){  
//在这里实现单例对象的功能  
}  
}
  • 底层会自动在静态代码块中让INSTANCE=new Singleton2("INSTTANCE",0); 并且INSTANCE是private static final的。
  • 枚举没有无参构造,并且底层不允许反射调用其构造方法,如果调用会抛出异常。
  • 枚举饿汉式能天然防止反射、反序列化破坏单例。虽然这种方法还没有广泛采用,但是单元素的枚举类型经常成为实现单例的最佳方法。
  • 如果单例必须扩展一个超类,而不是扩展 Enum 的时候,则不宜使用这个方法(虽然可以声明枚举去实现接口)。

懒汉式单例模式

/**  
* 饿汉式单例  
* 优点:  
* 线程安全:由于在类加载时就创建单例对象,因此不存在多线程环境下的同步问题。  
* 没有加锁的性能问题:饿汉式没有使用同步锁,因此不存在加锁带来的性能问题。  
* 实现简单:饿汉式的实现比较简单,不需要考虑多线程环境下的同步问题。  
* 缺点:  
* 立即加载:由于在类加载时就创建单例对象,因此可能会影响程序的启动速度。  
* 浪费资源:如果单例对象很大,并且程序中很少使用,那么饿汉式可能会浪费资源。  
*/  
public class HungryStyle implements Serializable {  
  
/**  
* 静态变量(单例对象),类加载时就初始化对象(不存在线程安全问题)  
*/  
private static final HungryStyle INSTANCE = new HungryStyle();  
  
private HungryStyle() {  
//保证反射不能创建对象  
if (INSTANCE != null) {  
throw new RuntimeException("单例对象不能重复创建");  
}  
}  
  
public HungryStyle getInstance() {  
return INSTANCE;  
}  
  
public Object readResolve(){  
return INSTANCE;  
}  
}

双重检查懒汉式

public class DoubleLockStyle {  
  
private DoubleLockStyle() {  
}  
  
/**  
* volatile关键字,使得instance变量在多个线程间可见,禁止指令重排序优化  
* volatile是一个轻量级的同步机制,即轻量锁  
*/  
private static volatile DoubleLockStyle INSTANCE = null;  
  
public static DoubleLockStyle getInstance() {  
//避免多个线程在已经又单例对象的情况下,来争抢锁资源  
if (INSTANCE == null) {  
//加锁是为了保证多线程情况下只创建一个对象  
synchronized (DoubleLockStyle.class) {  
if (INSTANCE == null) {  
//INSTANCE = new DoubleLockStyle();不是原子的,包括对象实例化,执行构造方法,给静态变量赋值,后面两步可能会被指令重排  
INSTANCE = new DoubleLockStyle();  
}  
}  
}  
return INSTANCE;  
}  
}