侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

实现线程安全的单例模式

2025-12-11 / 0 评论 / 4 阅读

题目

实现线程安全的单例模式

信息

  • 类型:问答
  • 难度:⭐⭐

考点

单例模式,线程安全,类变量与类方法,懒加载

快速回答

实现线程安全的单例模式需要:

  • 使用类变量存储唯一实例
  • 通过类方法提供全局访问点
  • 使用 Mutex 确保线程安全
  • 实现懒加载(首次调用时初始化)
  • new 设为私有方法防止外部实例化
## 解析

原理说明

单例模式确保一个类只有一个实例,并提供全局访问点。线程安全要求在多线程环境下:

  • 防止多次实例化
  • 避免返回未完全初始化的对象
  • 保证状态一致性

Ruby 中需使用 Mutex 同步临界区(实例化代码),结合双重检查锁定优化性能。

代码示例

require 'thread'

class Configuration
  @@instance = nil
  @@mutex = Mutex.new

  # 类方法提供全局访问点
  def self.instance
    return @@instance if @@instance

    @@mutex.synchronize do
      # 双重检查锁定:进入同步块后再次检查
      @@instance ||= new
    end

    @@instance
  end

  # 私有化构造函数
  private_class_method :new

  # 实例方法示例
  def settings
    @settings ||= load_settings
  end

  private

  def load_settings
    # 模拟耗时操作
    sleep(0.1)
    { env: 'production', timeout: 30 }
  end
end

# 测试线程安全
threads = []
5.times do
  threads << Thread.new { Configuration.instance }
end
threads.each(&:join)
puts "实例ID: #{Configuration.instance.object_id}" # 所有线程输出相同ID

最佳实践

  • 双重检查锁定:同步块外先检查实例,减少锁竞争
  • 懒加载:首次调用时初始化,避免启动开销
  • 私有构造函数:防止 new 直接创建实例
  • 模块化:复杂场景可使用 Singleton 模块(标准库)

常见错误

  • 缺少双重检查@@mutex.synchronize { @@instance ||= new } 未在同步块外检查实例,导致每次访问都加锁
  • 类变量未初始化:忘记设置 @@instance = nil
  • 非原子操作:未用互斥锁包裹实例化过程
  • 过早优化:在明确无并发场景下过度使用同步

扩展知识

  • GIL 的影响:MRI Ruby 有全局解释器锁,但 I/O 操作期间会释放锁,仍需同步控制
  • Singleton 模块:标准库简化实现(require 'singleton' + include Singleton
  • 依赖注入替代:单例可能增加耦合度,可考虑依赖注入容器
  • 序列化问题:单例序列化/反序列化时需重写 _dump_load 方法