单例模式概念: 单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供全局访问点。这对于需要在系统中共享某个资源(例如配置信息、数据库连接池等)的场景非常有用。
饿汉式(Eager Initialization): 在类加载时就创建实例,线程安全,但可能会造成资源浪费。你已经提供了一个经典的饿汉式实现。
懒汉式(Lazy Initialization): 在需要时才创建实例,可能存在线程安全问题,需要进行同步处理。你已经给出了懒汉式和双检锁懒汉式的实现,其中双检锁懒汉式通过双重检查锁定和volatile关键字确保了线程安全。
枚举单例模式: 利用枚举类型的特性,天然防止反射和反序列化攻击。你已经提供了一个枚举饿汉式的实现。
静态内部类单例模式: 使用静态内部类的方式来延迟加载实例,避免了同步问题,也能够天然防止反射和反序列化攻击。你已经给出了一个内部类懒汉式的实现。
为什么 DCL(Double-Checked Locking)中要使用 volatile: 在双检锁懒汉式中,volatile关键字确保了可见性和防止指令重排序。如果没有volatile,在多线程环境中,一个线程可能会看到一个未完全构造的实例,导致不正确的结果。
防止反射攻击: 在饿汉式和懒汉式中,通过在构造方法中加入特定逻辑,可以防止反射攻击。在枚举和静态内部类实现中,由于它们的特性,天然不容易受到反射攻击。
防止反序列化攻击: 在饿汉式和懒汉式中,通过实现readResolve方法可以防止反序列化攻击。在枚举实现中,由于枚举类型的特性,不容易受到反序列化攻击。
要求
public class Singleton1 implements Serializable {
private Singleton1() {
if (INSTANCE != null) {
throw new RuntimeException("单例对象不能重复创建");
}
System.out.println("private Singleton1()");
}
private static final Singleton1 INSTANCE = new Singleton1();
public static Singleton1 getInstance() {
return INSTANCE;
}
public static void otherMethod() {
System.out.println("otherMethod()");
}
public Object readResolve() {
return INSTANCE;
}
}
readResolve()
是防止反序列化破坏单例public enum Singleton2 {
INSTANCE;
private Singleton2() {
System.out.println("private Singleton2()");
}
@Override
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
public static Singleton2 getInstance() {
return INSTANCE;
}
public static void otherMethod() {
System.out.println("otherMethod()");
}
}
public class Singleton3 implements Serializable {
private Singleton3() {
System.out.println("private Singleton3()");
}
private static Singleton3 INSTANCE = null;
// Singleton3.class
public static synchronized Singleton3 getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton3();
}
return INSTANCE;
}
public static void otherMethod() {
System.out.println("otherMethod()");
}
}
public class Singleton4 implements Serializable {
private Singleton4() {
System.out.println("private Singleton4()");
}
private static volatile Singleton4 INSTANCE = null; // 可见性,有序性
public static Singleton4 getInstance() {
if (INSTANCE == null) {
synchronized (Singleton4.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton4();
}
}
}
return INSTANCE;
}
public static void otherMethod() {
System.out.println("otherMethod()");
}
}
为何必须加 volatile:
INSTANCE = new Singleton4()
不是原子的,分成 3 步:创建对象、调用构造、给静态变量赋值,其中后两步可能被指令重排序优化,变成先赋值、再调用构造INSTANCE == null
时发现 INSTANCE 已经不为 null,此时就会返回一个未完全构造的对象public class Singleton5 implements Serializable {
private Singleton5() {
System.out.println("private Singleton5()");
}
private static class Holder {
static Singleton5 INSTANCE = new Singleton5();
}
public static Singleton5 getInstance() {
return Holder.INSTANCE;
}
public static void otherMethod() {
System.out.println("otherMethod()");
}
}
JDK 中单例的体现
大家好,我是xwhking,一名技术爱好者,目前正在全力学习 Java,前端也会一点,如果你有任何疑问请你评论,或者可以加我QQ(2837468248)说明来意!希望能够与你共同进步