单例模式( 饿汉式与懒汉式 )
目录一、单例模式核心思想二、饿汉式单例类加载即初始化2.1 C 实现2.2 Java 实现2.3 饿汉式特点分析三、懒汉式单例延迟初始化按需创建3.1 C 实现3.2 Java 实现3.3 懒汉式的线程安全问题3.3.1 C 线程安全方案双检锁3.3.2 Java 线程安全方案静态内部类 / 枚举3.4 懒汉式特点分析四、饿汉式 vs 懒汉式核心对比五、单例模式的最佳实践六、总结在软件开发中单例模式Singleton Pattern是创建型设计模式的核心之一它保证一个类仅有一个实例并提供一个全局访问点避免重复创建对象带来的资源浪费同时保证状态一致性。本文将结合 C 和 Java 代码深入解析单例模式的两种经典实现饿汉式Eager Initialization与懒汉式Lazy Initialization帮你彻底理解其原理、差异与工程实践。一、单例模式核心思想单例模式的核心目标只有两个控制实例数量确保一个类在运行期间最多只存在一个实例。全局访问提供一个全局入口让任何地方都能获取到这个唯一实例。为了实现这两个目标单例模式必须满足三个约束构造函数私有化禁止外部通过new直接创建实例。禁用拷贝构造与赋值运算符防止实例被复制破坏唯一性。提供静态访问方法作为获取实例的唯一全局入口。二、饿汉式单例类加载即初始化饿汉式的核心思想是 **“急切初始化”**—— 在类加载阶段就创建好唯一实例不管后续是否会使用它就像 “饿汉” 一样迫不及待。2.1 C 实现#pragma once #include iostream // 饿汉模式 class Bully { public: // 禁用拷贝构造和赋值运算符 Bully(const Bully) delete; Bully operator(const Bully) delete; // 全局访问点返回唯一实例的引用 static Bully get() { return person; } private: // 私有构造函数禁止外部实例化 Bully() {} // 静态成员变量类加载时初始化 static Bully person; }; // 类外初始化静态成员必须自动调用构造函数 Bully Bully::person;2.2 Java 实现// 恶汉模式饿汉式 class SingletonBully { // 类加载时即创建实例private 保证外部无法直接访问 private static SingletonBully instance new SingletonBully(); // 私有构造函数 private SingletonBully() {} // 全局访问点 public static SingletonBully getInstance() { return instance; } }2.3 饿汉式特点分析优点缺点✅ 实现简单代码清晰❌ 类加载时即初始化可能造成内存浪费若实例从未使用✅ 天生线程安全类加载由 JVM/C 运行时保证❌ 无法延迟加载灵活性较低✅ 无锁性能极高❌ 不适合实例创建成本高、使用频率低的场景三、懒汉式单例延迟初始化按需创建懒汉式的核心思想是 **“延迟初始化”**—— 只有在第一次获取实例时才创建对象就像 “懒汉” 一样不到万不得已不干活。3.1 C 实现#pragma once #include iostream // 懒汉模式 class Fed { public: // 禁用拷贝构造和赋值运算符 Fed(const Fed) delete; Fed operator(const Fed) delete; // 全局访问点第一次调用时创建实例 static Fed get() { if (instance nullptr) { instance new Fed(); } return *instance; } private: // 私有构造函数 Fed() {} // 静态指针初始为 nullptr延迟初始化 static Fed* instance; }; // 类外初始化静态指针 //static Fed* instance nullptr; Fed* Fed::instance nullptr;⚠️ 注意static Fed* instance nullptr;多写了一个static正确写法是Fed* Fed::instance nullptr;。3.2 Java 实现// 懒汉模式 class SingletonFed { // 静态变量初始为 null延迟初始化 private static SingletonFed instance; // 私有构造函数 private SingletonFed() {} // 全局访问点第一次调用时创建实例 public static SingletonFed getInstance() { if (instance null) { instance new SingletonFed(); } return instance; } }3.3 懒汉式的线程安全问题基础版懒汉式在单线程环境下是安全的但在多线程环境下会出现问题多个线程同时进入if (instance null)判断可能导致多个实例被创建破坏单例约束。3.3.1 C 线程安全方案双检锁#include mutex class Fed { public: static Fed get() { // 第一次检查无锁提高性能 if (instance nullptr) { // 加锁保证只有一个线程能进入创建逻辑 std::lock_guardstd::mutex lock(mtx); // 第二次检查防止多线程重复创建 if (instance nullptr) { instance new Fed(); } } return *instance; } private: static Fed* instance; static std::mutex mtx; // 互斥锁 }; Fed* Fed::instance nullptr; std::mutex Fed::mtx;3.3.2 Java 线程安全方案静态内部类 / 枚举静态内部类推荐利用类加载机制实现延迟初始化与线程安全。class SingletonFed { private SingletonFed() {} // 静态内部类只有在被调用时才会加载延迟初始化 private static class Holder { private static final SingletonFed INSTANCE new SingletonFed(); } public static SingletonFed getInstance() { return Holder.INSTANCE; } }枚举最安全Java 枚举天然保证单例且防止反射与序列化破坏单例。enum SingletonFed { INSTANCE; public void doSomething() { // 业务逻辑 } }3.4 懒汉式特点分析优点缺点✅ 延迟加载只有在第一次使用时才创建实例节省内存❌ 基础版线程不安全需要额外处理加锁 / 静态内部类✅ 适合实例创建成本高、使用频率低的场景❌ 加锁版本会带来一定性能开销✅ 灵活性高可根据业务场景调整初始化时机❌ 实现复杂度略高于饿汉式四、饿汉式 vs 懒汉式核心对比对比维度饿汉式懒汉式实例创建时机类加载阶段第一次获取实例时线程安全天生安全基础版不安全需额外处理内存占用类加载时即占用可能浪费延迟占用按需分配性能无锁性能极高加锁版本性能略低实现复杂度简单略复杂需处理线程安全适用场景实例小、使用频繁实例大、使用频率低五、单例模式的最佳实践优先考虑饿汉式如果实例创建成本低、使用频繁饿汉式是最简单高效的选择。延迟加载用懒汉式如果实例创建成本高如连接池、大对象且可能不会被使用选择懒汉式。Java 推荐枚举 / 静态内部类枚举是最安全的单例实现静态内部类是延迟初始化的优雅方案。C 推荐 C11 静态局部变量C11 后静态局部变量的初始化是线程安全的可极简实现懒汉式class Fed { public: static Fed get() { static Fed instance; // 线程安全的延迟初始化 return instance; } private: Fed() {} };警惕单例的滥用单例模式会引入全局状态增加代码耦合度不利于单元测试需谨慎使用。六、总结单例模式是软件开发中最常用的设计模式之一饿汉式和懒汉式是其两种核心实现饿汉式以空间换时间简单高效线程安全。懒汉式以时间换空间延迟加载需处理线程安全。在实际开发中应根据业务场景选择合适的实现方式同时注意单例模式的局限性避免过度使用。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2435668.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!