饿汉式
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
- 线程安全性:线程安全,因为类加载由 JVM 保证线程安全,在类加载时实例就已创建好,后续调用
getInstance
不会再创建新实例。
- 性能:性能较好,因为
getInstance
方法没有任何锁操作,直接返回已创建的实例。
- 资源占用:可能会造成资源浪费,如果这个单例对象一直没有使用,但在类加载时就已创建。
懒汉式(线程不安全)
- 实现方式:在第一次调用
getInstance
方法时才创建实例,代码如下:
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
- 线程安全性:线程不安全,在多线程环境下,当多个线程同时判断
instance == null
时,可能会创建多个实例。
- 性能:性能较好,只有在需要时才创建实例。但在多线程环境下,需额外处理线程安全问题,可能会影响性能。
- 资源占用:资源利用率高,只有在真正使用时才占用资源创建实例。
懒汉式(线程安全,同步方法)
- 实现方式:通过给
getInstance
方法加 synchronized
关键字来保证线程安全,代码如下:
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
- 线程安全性:线程安全,
synchronized
关键字保证了同一时间只有一个线程能进入 getInstance
方法创建实例。
- 性能:性能较差,因为每次调用
getInstance
方法都需要获取锁,而获取锁和释放锁的操作开销较大。
- 资源占用:资源利用率高,同样是在需要时才创建实例。
双重检查锁(DCL)
- 实现方式:在
synchronized
块内再进行一次 null
判断,代码如下:
public class Singleton {
private volatile static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
- 线程安全性:线程安全,
volatile
关键字保证了 instance
变量的可见性,避免指令重排序问题,同时双重 null
判断和 synchronized
块保证了在多线程环境下只会创建一个实例。
- 性能:性能较好,只有在第一次创建实例时会有锁操作,后续调用
getInstance
方法无需获取锁,直接返回已创建的实例。
- 资源占用:资源利用率高,延迟加载,只有在需要时才创建实例。
静态内部类
- 实现方式:利用静态内部类的特性,在外部类被加载时,静态内部类不会被加载,只有在调用
getInstance
方法时,静态内部类才会被加载并创建实例,代码如下:
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
- 线程安全性:线程安全,类加载由 JVM 保证线程安全,静态内部类在第一次使用时才会被加载,且只会加载一次,保证了实例的唯一性。
- 性能:性能较好,
getInstance
方法没有任何锁操作,直接返回已创建的实例。
- 资源占用:资源利用率高,延迟加载,只有在调用
getInstance
方法时才会占用资源创建实例。