You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

单例模式仅初始化时加锁是否足够?线程安全存疑

关于单例模式双重检查锁的线程安全误区

你提到的问题完全正确,双重检查锁(Double-Checked Locking Pattern, DCLP)仅负责保证单例实例初始化过程的线程安全,它无法解决实例内部共享状态被多线程并发修改时的竞态问题。

问题根源

当多个线程成功获取到同一个单例实例后,对实例内的共享变量执行「读取旧值→计算→写入新值」这类非原子操作时,若没有额外的同步机制,就会出现你描述的情况:

  • 线程1读取共享变量后被中断
  • 线程2用同一旧值完成修改并写入
  • 线程1恢复后用旧值计算,直接覆盖线程2的修改,导致数据不一致

解决方法

要解决这个问题,需要针对单例内部的共享状态操作单独添加同步机制,常见的两种方案如下:

方案1:给共享操作加互斥锁

通过实例内部的互斥锁,保证每次只有一个线程能修改共享变量:

#include <mutex>

class Singleton {
private:
    static Singleton* instance;
    static std::mutex init_mutex_; // 仅用于初始化的锁
    std::mutex state_mutex_; // 保护内部共享状态的锁
    int shared_val_ = 1; // 示例共享变量

public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            std::lock_guard<std::mutex> lock(init_mutex_);
            if (instance == nullptr) {
                instance = new Singleton;
            }
        }
        return instance;
    }

    void Multiply2() {
        std::lock_guard<std::mutex> lock(state_mutex_);
        shared_val_ *= 2;
    }

    void Multiply3() {
        std::lock_guard<std::mutex> lock(state_mutex_);
        shared_val_ *= 3;
    }
};

// 静态成员初始化
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::init_mutex_;

方案2:使用原子变量(适合简单数值操作)

利用C++的std::atomic类型,通过CAS(Compare-And-Swap)操作实现无锁的原子修改:

#include <mutex>
#include <atomic>

class Singleton {
private:
    static Singleton* instance;
    static std::mutex init_mutex_;
    std::atomic<int> shared_val_ = 1;

public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            std::lock_guard<std::mutex> lock(init_mutex_);
            if (instance == nullptr) {
                instance = new Singleton;
            }
        }
        return instance;
    }

    void Multiply2() {
        int old_val = shared_val_.load(std::memory_order_relaxed);
        // CAS循环直到操作成功
        while (!shared_val_.compare_exchange_weak(old_val, old_val * 2)) {}
    }

    void Multiply3() {
        int old_val = shared_val_.load(std::memory_order_relaxed);
        while (!shared_val_.compare_exchange_weak(old_val, old_val * 3)) {}
    }
};

// 静态成员初始化
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::init_mutex_;

关键提醒

很多开发者容易混淆两个不同的线程安全场景:

  • 单例实例的初始化安全:由DCLP解决
  • 单例实例内部状态的并发修改安全:需要额外的同步措施(锁/原子变量)

这是两个独立的问题,DCLP不会自动覆盖后者的线程安全需求。

内容的提问来源于stack exchange,提问作者Ali K.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 17:25:09