延迟加载(Lazy Initialization)的单例模式是一种在对象第一次被请求时才创建单例实例的设计模式。这种方法可以减少程序启动时的负载和启动时间,特别是当单例对象的创建开销较大或者在启动时不一定需要该对象时。
下面是实现延迟加载单例模式的几种常见方法:
1. 懒汉式(线程不安全)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
这种实现最简单,但它不是线程安全的。如果多个线程同时访问getInstance()
方法,可能会创建多个实例。
2. 懒汉式(线程安全)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
通过添加synchronized
关键字,可以使得该方法线程安全,但这会显著降低方法的性能。
3. 双重检查锁定(Double-Checked Locking)
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
双重检查锁定既可以保证线程安全,也可以减少同步带来的性能影响。volatile
关键字确保instance
变量的写操作对其他线程立即可见,防止指令重排。
4. 静态内部类(推荐)
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
这种方式利用了Java的类加载机制来保证单例实例的唯一性和线程安全。只有当getInstance()
方法第一次被调用时,SingletonHolder
才会被加载和初始化,这时单例类的实例才会被创建。
5. 枚举实现(最简洁,自动支持序列化机制,绝对防止多次实例化)
public enum Singleton {
INSTANCE;
public void doSomething() {
// Perform operation here
}
}
这是实现单例模式的最佳方法之一,因为它更简洁,自动支持序列化机制,防止多次实例化,即使在面对复杂的序列化或反射攻击时。
在实际应用中,选择哪种方式实现延迟加载的单例模式取决于具体需求和场景。如果对性能要求不是很高,并且可以接受synchronized
带来的性能开销,可以选择线程安全的懒汉式。如果对性能要求较高,可以使用双重检查锁定或静态内部类方法。如果需要防止序列化破坏单例,可以使用枚举方式实现单例。