2026/4/2 6:56:40
网站建设
项目流程
深圳市网站建设平台,东莞市建设网,施工企业质量发展规划,建设seo网站单例模式的核心原则
实现单例的核心要求#xff1a;
私有构造方法#xff08;防止外部通过new创建实例#xff09;#xff1b;类内部创建唯一实例#xff1b;提供公共静态方法获取该实例#xff1b;保证多线程环境下实例唯一#xff08;线程安全#xff09;。 写法 1…单例模式的核心原则实现单例的核心要求私有构造方法防止外部通过new创建实例类内部创建唯一实例提供公共静态方法获取该实例保证多线程环境下实例唯一线程安全。写法 1饿汉式静态常量—— 最简单的线程安全单例代码实现/** * 饿汉式静态常量 * 特点线程安全、无懒加载、类加载时就初始化实例 */ public class Singleton1 { // 1. 私有构造方法禁止外部new private Singleton1() {} // 2. 类加载时就创建唯一实例饿汉提前初始化 private static final Singleton1 INSTANCE new Singleton1(); // 3. 提供公共方法获取实例 public static Singleton1 getInstance() { return INSTANCE; } }解析线程安全类加载过程由 JVM 保证线程安全静态变量初始化在类加载的clinit方法中JVM 会保证该方法被加锁多线程下只会执行一次优点实现简单、无线程安全问题、性能高缺点无懒加载类加载时就初始化实例如果实例创建成本高如占内存大、依赖外部资源且程序全程未使用该实例会造成资源浪费适用场景实例创建成本低、程序启动后大概率会使用的单例。写法 2饿汉式静态代码块—— 初始化逻辑复杂的饿汉式代码实现/** * 饿汉式静态代码块 * 特点线程安全、无懒加载适合初始化逻辑复杂的场景 */ public class Singleton2 { private Singleton2() {} // 先声明实例静态代码块中初始化适合需要读取配置文件等复杂逻辑 private static Singleton2 INSTANCE; static { // 可添加复杂初始化逻辑如读取配置、连接数据库等 INSTANCE new Singleton2(); } public static Singleton2 getInstance() { return INSTANCE; } }解析核心逻辑和「静态常量饿汉式」一致只是把实例初始化放到静态代码块中适用场景单例初始化需要执行复杂逻辑如读取配置文件、初始化资源时。写法 3懒汉式线程不安全—— 笔试常考 “反面教材”代码实现/** * 懒汉式线程不安全 * 特点懒加载使用时才初始化但多线程下会创建多个实例实际开发禁止使用 */ public class Singleton3 { private Singleton3() {} private static Singleton3 INSTANCE; // 调用方法时才初始化懒加载 public static Singleton3 getInstance() { if (INSTANCE null) { // 多线程下此处会并发执行创建多个实例 INSTANCE new Singleton3(); } return INSTANCE; } }解析线程不安全多线程同时调用getInstance()时会同时进入if (INSTANCE null)创建多个实例破坏单例优点懒加载节省资源缺点线程不安全仅适用于单线程环境笔试考点面试官常问 “这个写法有什么问题”需答出 “多线程下会创建多个实例”。写法 4懒汉式线程安全同步方法—— 安全但低效代码实现/** * 懒汉式同步方法 * 特点线程安全、懒加载但每次调用getInstance都加锁效率低 */ public class Singleton4 { private Singleton4() {} private static Singleton4 INSTANCE; // 加synchronized保证线程安全但每次调用都锁整个方法效率低 public static synchronized Singleton4 getInstance() { if (INSTANCE null) { INSTANCE new Singleton4(); } return INSTANCE; } }解析线程安全synchronized修饰静态方法保证多线程下只有一个线程能进入方法优点懒加载、线程安全缺点效率极低 —— 即使实例已创建后续所有调用getInstance()的线程都要排队获取锁浪费资源适用场景单例调用频率极低的场景几乎不用。写法 5双重校验锁DCL—— 实际开发最常用代码实现/** * 双重校验锁DCLDouble Check Lock * 特点线程安全、懒加载、效率高实际开发首选 */ public class Singleton5 { private Singleton5() {} // 关键volatile修饰防止指令重排序 private static volatile Singleton5 INSTANCE; public static Singleton5 getInstance() { // 第一次校验实例已创建则直接返回无需加锁提高效率 if (INSTANCE null) { synchronized (Singleton5.class) { // 锁类对象保证只有一个线程进入 // 第二次校验防止多个线程等待锁后重复创建实例 if (INSTANCE null) { INSTANCE new Singleton5(); } } } return INSTANCE; } }核心解析笔试高频考点为什么需要两次校验第一次校验外层if实例已创建时直接返回避免进入同步块提高效率第二次校验内层if假设两个线程同时通过外层if等待锁后只有一个线程能进入同步块第二个线程进入时实例已创建避免重复初始化。为什么需要 volatileINSTANCE new Singleton5()不是原子操作JVM 会拆分为 3 步① 分配内存空间② 初始化实例③ 把 INSTANCE 指向分配的内存地址。若无 volatileJVM 可能发生指令重排序①→③→②导致线程 A 执行完①③INSTANCE 已非 null但实例未初始化线程 B 进入外层if发现 INSTANCE≠null直接返回未初始化的实例引发空指针异常。volatile 禁止指令重排序保证①→②→③的执行顺序避免上述问题。优缺点优点线程安全、懒加载、效率高仅初始化时加锁后续无锁缺点实现稍复杂需注意 volatile 不能省略适用场景绝大多数实际开发场景如工具类、连接池单例。写法 6静态内部类 —— 优雅的懒加载单例代码实现/** * 静态内部类单例 * 特点线程安全、懒加载、实现优雅《Effective Java》推荐写法之一 */ public class Singleton6 { private Singleton6() {} // 静态内部类不会随外部类加载而初始化只有调用getInstance时才加载 private static class SingletonHolder { // JVM保证静态常量初始化的线程安全 private static final Singleton6 INSTANCE new Singleton6(); } public static Singleton6 getInstance() { // 调用此方法时才加载SingletonHolder创建INSTANCE return SingletonHolder.INSTANCE; } }解析懒加载外部类Singleton6加载时内部类SingletonHolder不会加载只有调用getInstance()时内部类才加载创建实例线程安全JVM 保证静态内部类的clinit方法线程安全实例只会创建一次优缺点优点线程安全、懒加载、实现优雅无需手动加锁 /volatile缺点无法防止反射破坏单例所有非枚举单例都有此问题。写法 7枚举单例 —— 最安全的单例《Effective Java》推荐代码实现/** * 枚举单例 * 特点天然线程安全、防止反射/序列化破坏单例最简单的完美单例 */ public enum Singleton7 { // 唯一实例枚举的每个常量都是单例 INSTANCE; // 可添加单例的业务方法 public void doSomething() { System.out.println(枚举单例执行方法); } } // 使用方式 // Singleton7.INSTANCE.doSomething();核心优势笔试必答天然线程安全枚举类的初始化由 JVM 保证多线程下只会创建一个实例防止反射破坏反射无法创建枚举实例Constructor.newInstance()会抛异常防止序列化破坏枚举的序列化由 JVM 处理反序列化不会创建新实例实现最简单无需手动处理锁、volatile 等代码极简。优缺点优点绝对线程安全、防反射 / 序列化、实现简单缺点无懒加载枚举类加载时就初始化实例适用场景需要绝对安全的单例场景如配置中心、核心工具类。总结写法线程安全懒加载防反射 / 序列化适用场景饿汉式静态常量✅❌❌实例创建成本低、必用的单例懒汉式同步方法✅✅❌调用频率极低的单例几乎不用双重校验锁DCL✅✅❌绝大多数实际开发场景静态内部类✅✅❌追求优雅实现的懒加载单例枚举单例✅❌✅要求绝对安全的单例关键点回顾笔试高频考点DCL 写法中 volatile 的作用、双重校验的原因、枚举单例的优势实际开发优先选DCL需懒加载、枚举单例需绝对安全、静态内部类追求优雅所有非枚举单例的构造方法私有仍可通过反射暴力破解设置setAccessible(true)枚举单例可彻底避免。