01-单例模式
1.单例模式-饿汉式
- 饿汉式单例模式在类加载的时候就会将对象创建完成并且进行实例化,所以创建的过程是线程安全的,但是不支持延时加载。
- Java代码。
public class Singleton {
private static final Singleton SINGLETON = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return SINGLETON;
}
}
2.单例模式-懒汉式
- 第一种懒汉式。
/**
* 单例模式-懒汉式,这种写法有一个明显的缺点,getInstance()函数被添加了一把锁-synchronized,
* 导致这个函数的并发度变为1,即同一时间只允许一个线程执行getInstance()函数。
* 如果Singleton类被频繁使用,频繁的加锁、释放锁,就会产生性能问题。
*/
public class Singleton {
private static Singleton SINGLETON = null;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (SINGLETON == null) {
SINGLETON = new Singleton();
}
return SINGLETON;
}
}
- 第二种懒汉式。
/**
* 单例模式-懒汉式,双重检索。
* 存在的问题:CPU指令重排序可能会导致Singleton类的对象被关键字new创建并赋值给SINGLETON之后,
* 还没有来得急初始化(执行构造函数中的代码逻辑),就被另一个线程使用了。这样会导致另一个线程使用了
* 一个没有完整初始化的Singleton类的对象。
*
* 可以通过给SINGLETON成员变量SINGLETON添加volatile关键字禁止指令重排序来解决这个问题。
*/
public class Singleton {
private static Singleton SINGLETON = null;
private Singleton() {}
public static Singleton getInstance() {
if (SINGLETON == null) {
synchronized (Singleton.class) {
if (SINGLETON == null) {
SINGLETON = new Singleton();
}
}
}
return SINGLETON;
}
}
- 第三种懒汉式。
/**
* 可以通过给SINGLETON成员变量SINGLETON添加volatile关键字来禁止指令重排序。
*/
public class Singleton {
private static volatile Singleton SINGLETON = null;
private Singleton() {}
public static Singleton getInstance() {
if (SINGLETON == null) {
synchronized (Singleton.class) {
if (SINGLETON == null) {
SINGLETON = new Singleton();
}
}
}
return SINGLETON;
}
}
- 第四种懒汉式,静态内部类。
/**
* SingletonHolder是一个静态内部类,当外部类Singleton被加载时,并不会加载
* SingletonHolder类。
*
* 只有当第一次调用getInstance()函数时,SingletonHolder类才会被加载,
* 也才会去创建Singleton类的实例对象。
*
* SINGLETON的唯一性和创建过程的线程安全性都有JVM来保证。
*/
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton SINGLETON = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.SINGLETON;
}
}
- 第五种懒汉式,枚举。
public enum Singleton {
INSTANCE
}
3.单例模式总结
- 懒汉式支持延时加载,但并非是最好的选择。如果单例实例初始化占用的资源较多(占用内存较多)或者初始化时间较长(如需要加载各种配置文件),也可以考虑使用饿汉式的单例模式。
- 如果初始化时间较长,等真正使用时在初始化,会影响系统性能,导致接口响应时间变长。
- 如果初始化单例实例占用的资源较多,那么,按照fail-fast设计原则(有问题及早暴露)。在程序启动的时候将实例化的操作完成,如果资源不够就触发报错(如Java中的PermGen Space OOM),避免在运行时报错,影响系统的正常使用。