今日面试题 :单例模式有哪几种实现?如何保证线程安全?

大家好,我是鸭鸭。

刷题就上面试鸭,帮助大家更高效通过面试(支持网页端、小程序):https://mianshiya.com/

祝大家都能拿到心仪的 Offer!

回答重点

单例模式常见有:饿汉式、懒汉式(线程安全版需要加双重检查锁定)、静态内部类、枚举单例(Java 特有) 等几个实现方式。

  • 饿汉式:实例在类加载时就创建,线程安全,但如果实例初始化较重或没有被使用会浪费资源。
  • 懒汉式:实例在首次访问时创建,节约资源,但需要确保线程安全。
  • 双重检查锁定:在懒汉式的基础上优化,直接加锁效率太低,双重检查锁只在第一次检查实例为空时加锁,提高性能。
  • 静态内部类:利用类加载机制实现懒加载和线程安全,推荐使用。
  • 枚举单例(Java 特有):通过枚举实现单例,简单且防止反射和序列化攻击。

如何保证线程安全?

  • Java:推荐静态内部类或双重检查锁定,配合 volatile。
  • C++:推荐使用 std::call_oncemutex 实现线程安全。
  • Python:使用 __new__ 方法或装饰器实现,配合线程锁确保安全。

扩展知识

为什么需要双重检查锁定?

因为懒汉式单例在多线程环境下,可能出现多个线程同时初始化实例的问题,最简单是加个锁:

java
复制代码
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }

但是这样一来,每次调用 getInstance 方法都需要加锁,而实际上只需要在第一次创建实例时加锁。在高并发环境下,频繁获取单例对象的锁操作会显著降低性能。

而双重检查锁定通过减少加锁的范围,避免了每次获取实例时都加锁的问题,提升了性能。

java
复制代码
public class Singleton { private static volatile Singleton instance; // 使用 volatile 防止指令重排 private Singleton() {} public static Singleton getInstance() { if (instance == null) { // 第一次检查:避免不必要的同步 synchronized (Singleton.class) { if (instance == null) { // 第二次检查:确保实例唯一 instance = new Singleton(); } } } return instance; } }

为什么双重检查锁定需要 volatile 关键字?

在 Java 中,volatile 修饰符用于防止指令重排序,从而确保双重检查锁定的正确性。

instance = new Singleton() 是一个非原子操作,它分为以下三步:

  1. 分配内存空间。
  2. 初始化对象。
  3. 将对象的引用赋值给 instance

在没有 volatile 的情况下,编译器和 CPU 可能会对这些步骤进行重排序(比如执行顺序变成 1 → 3 → 2)。此时,另一个线程可能会在 instance 被赋值后,但对象尚未完成初始化时访问它,从而导致错误。

所以将 instance 声明为 volatile,可以禁止指令重排序,确保对象的初始化过程对所有线程可见。

java
复制代码
private static volatile Singleton instance;

保证 instance 的写操作对其他线程立即可见,并禁止重排序优化,确保双重检查锁定的正确性。

总结

  • 双重检查锁定通过缩小加锁范围,仅在必要时同步代码块,既保证线程安全,又提升性能。
  • volatile 关键字是实现双重检查锁定的关键,防止指令重排序导致未初始化对象被访问。

Java 单例实现代码示例

饿汉式(线程安全,类加载时初始化)

java
复制代码
public class Singleton { private static final Singleton instance = new Singleton(); private Singleton() {} public static Singleton getInstance() { return instance; } }

懒汉式(线程不安全,需改进)

java
复制代码
public class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }

双重检查锁定(线程安全,推荐)

java
复制代码
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; } }

静态内部类(线程安全,推荐)

java
复制代码
public class Singleton { private Singleton() {} private static class Holder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return Holder.INSTANCE; } }

枚举单例

java
复制代码
public enum Singleton { INSTANCE; public void bizMethod() { // 一些业务逻辑方法 } } //使用 Singleton singleton = Singleton.INSTANCE; singleton.bizMethod();

C++ 单例实现代码示例

饿汉式单例

cpp
复制代码
#include <iostream> class Singleton { private: static Singleton instance; // 静态实例直接初始化 Singleton() {} // 私有构造函数 public: // 禁止拷贝和赋值操作 Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; static Singleton& getInstance() { return instance; // 返回静态实例 } void doSomething() { std::cout << "Eager Singleton instance" << std::endl; } }; // 静态成员变量定义和初始化 Singleton Singleton::instance; int main() { Singleton& singleton = Singleton::getInstance(); singleton.doSomething(); return 0; }

懒汉式(线程不安全,需改进)

cpp
复制代码
#include <iostream> class Singleton { private: static Singleton* instance; // 静态实例指针 Singleton() {} // 私有构造函数 public: // 禁止拷贝和赋值操作 Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; static Singleton* getInstance() { if (instance == nullptr) { // 只有在实例为空时创建 instance = new Singleton(); } return instance; } void doSomething() { std::cout << "Lazy Singleton instance (Not Thread Safe)" << std::endl; } }; // 初始化静态成员变量 Singleton* Singleton::instance = nullptr; int main() { Singleton* singleton = Singleton::getInstance(); singleton->doSomething(); return 0; }

双重检查锁定(线程安全)

cpp
复制代码
#include <iostream> #include <mutex> class Singleton { private: static Singleton* instance; static std::mutex mtx; Singleton() {} public: Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; static Singleton* getInstance() { if (instance == nullptr) { std::lock_guard<std::mutex> lock(mtx); if (instance == nullptr) { instance = new Singleton(); } } return instance; } void doSomething() { std::cout << "Double-Checked Locking Singleton instance" << std::endl; } }; Singleton* Singleton::instance = nullptr; std::mutex Singleton::mtx; int main() { Singleton* singleton = Singleton::getInstance(); singleton->doSomething(); return 0; }

静态局部变量(C++11 推荐)

cpp
复制代码
#include <iostream> class Singleton { private: Singleton() {} // 私有构造函数 public: // 禁止拷贝和赋值操作 Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; static Singleton& getInstance() { static Singleton instance; // 静态局部变量,线程安全 (C++11 保证) return instance; } void doSomething() { std::cout << "Static Local Variable Singleton instance" << std::endl; } }; int main() { Singleton& singleton = Singleton::getInstance(); singleton.doSomething(); return 0; }

Python 单例实现代码示例

懒汉式(线程不安全)

python
复制代码
class Singleton: _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs) return cls._instance # 使用示例 singleton = Singleton()

线程安全的单例

python
复制代码
import threading class Singleton: _instance = None _lock = threading.Lock() def __new__(cls, *args, **kwargs): with cls._lock: if not cls._instance: cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs) return cls._instance # 使用示例 singleton1 = Singleton() singleton2 = Singleton() print(singleton1 is singleton2) # 输出 True

使用装饰器实现单例

python
复制代码
def singleton(cls): instances = {} def get_instance(*args, **kwargs): if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return get_instance @singleton class Singleton: pass # 使用示例 singleton1 = Singleton() singleton2 = Singleton() print(singleton1 is singleton2) # 输出 True
0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
编程导航鸭鸭
下载 APP