饿汉式
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
- 原理:类加载机制保证了INSTANCE只会被创建一次,由于类加载由JVM保证线程安全,所以饿汉式天生线程安全。
- 优点:实现简单,线程安全,在类加载时就完成初始化,不会存在多线程并发访问的问题。
- 缺点:如果单例实例在整个应用生命周期中不一定会被使用,那么类加载时就创建实例会造成资源浪费。
懒汉式(线程不安全)
- 实现方式:在第一次调用
getInstance
方法时才创建实例。
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
- 原理:延迟加载,只有在需要时才创建实例。
- 优点:实现简单,延迟加载,避免了不必要的资源浪费。
- 缺点:线程不安全,在多线程环境下可能会创建多个实例。
懒汉式(线程安全,同步方法)
- 实现方式:对
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)
- 实现方式:两次检查
INSTANCE
是否为null
,并在同步块内再次检查。
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;
}
}
- 原理:第一次
if (INSTANCE == null)
检查是为了避免不必要的同步,当INSTANCE
已经创建时,直接返回实例。同步块内的第二次if (INSTANCE == null)
检查是为了防止多个线程同时通过第一次检查,在同步块内再次创建实例。volatile
关键字保证了INSTANCE
的可见性,防止指令重排导致的初始化不完全问题。
- 优点:既保证了线程安全,又提高了性能,只有在第一次创建实例时会进行同步操作。
- 缺点:实现相对复杂,需要正确使用
volatile
关键字,并且在早期JVM版本中可能存在指令重排问题导致单例创建失败。
静态内部类
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
- 原理:静态内部类只有在调用
getInstance
方法时才会被加载,类加载机制保证了INSTANCE
只会被创建一次,从而实现了延迟加载和线程安全。
- 优点:实现相对简单,延迟加载,线程安全,利用了类加载机制保证单例的唯一性。
- 缺点:在一些特殊场景下,如序列化和反射攻击时,可能会破坏单例模式。
枚举
public enum Singleton {
INSTANCE;
// 可以在这里添加其他方法和属性
}
- 原理:枚举类型在Java中是线程安全的,并且只会被实例化一次。
- 优点:实现简单,线程安全,天然防止反射和序列化攻击。
- 缺点:不太符合传统的单例模式实现方式,在某些情况下可能不够灵活,例如不能继承其他类。