当单例遇到多线程

java中单例模式是一种常见的设计模式,一直以为单例模式很简单。

最近在工作中遇到了问题,才发现当单例遇到多线程很容易会出现多个实例。

网上一查才发现,写法有问题,不支持多线程。

public class Singleton {
    private static Singleton instance;
    private Singleton (){}

    public static Singleton getInstance() {
	if (instance == null) {
	    instance = new Singleton();
	}
	return instance;
    }
}

需要加线程同步,一种比较简单的写法:

public class Singleton {
   private static Singleton instance = null;  
    private Singleton() { }

   public static synchronized Singleton getInstance() {
      if(instance == null) {
         instance = new Singleton();
      } 
       return instance;
   }
}

这样解决了线程安全问题,但是每个线程调用getInstance都要加锁,我们想要只在第一次调用getInstance时加锁,明显这样会影响多线程的效率。

继续优化:

public class Singleton {
   private static Singleton instance = null;
   private Singleton() { }
   public static Singleton getInstance() {
      if(instance == null) {
         synchronzied(Singleton.class) {
            Singleton temp = instance;
            if(temp == null) {
               temp = new Singleton();
               instance = temp
            }
         }
      }
      return instance;
   }
}

注意不能写成下面这样:(这样会有指令重排序问题

public class Singleton {
   private static Singleton instance = null;
   private Singleton() { }
   public static Singleton getInstance() {
      if(instance == null) {
         synchronzied(Singleton.class) {
            if(instance == null) {
               instance = new Singleton();
            }
         }
      }
      return instance;
   }
}
虽然可以用volatile来避免指令重排序问题。但是不推荐这样写。(还是存在漏洞)
public class Singleton {
   private static volatile Singleton instance = null;
   private Singleton() { }
   public static Singleton getInstance() {
      if(instance == null) {
         synchronzied(Singleton.class) {
            if(instance == null) {
               instance = new Singleton();
            }
         }
      }
      return instance;
   }
}


到最后还是老老实实的采用了双重检查,加线程同步,不用volatile的写法:

public class Singleton {
   private static Singleton instance = null;
   private Singleton() { }
   public static Singleton getInstance() {
      if(instance == null) {
         synchronzied(Singleton.class) {
            Singleton temp = instance;
            if(temp == null) {
               temp = new Singleton();
               instance = temp
            }
         }
      }
      return instance;
   }
}

如果哪位高手有更好的写法,请多指教。
<think>好的,我现在需要帮助用户了解C++中实现模式多线程安全的最简方法。首先,我得回忆下模式的基本概念。模式确保一个类只有一个实,并提供全局访问点。但在多线程环境下,可能会遇到多个线程同时创建实问题,导致多次实化,破坏原则。 用户提到的是“最简的方法”,所以需要找到最直接有效的解决方案。我记得传统的懒汉式在第一次调用时才创建实,但在多线程下不安全。那如何保证线程安全呢?常见的方法有加锁、内部静态变量、C++11后的局部静态变量特性等。 首先想到的是双重检查锁定(Double-Checked Locking),它通过在加锁前后两次检查实是否存在,减少锁的使用次数,提高性能。但以前在C++中可能因为指令重排导致问题,不过在C++11之后,有了内存模型的支持,可以用原子变量来解决。但用户要最简的方法,可能双重检查虽然高效,但实现起来稍微复杂一些。 然后想到C++11中的局部静态变量特性。根据标准,局部静态变量的初始化是线程安全的。编译器会保证静态变量在多线程环境下的初始化只进行一次。因此,使用局部静态变量来实现模式可能是最简的。这种方法不需要显式的锁,代码简洁。 那具体如何实现呢?通常,模式的构造函数是私有的,提供一个公共的静态方法获取实。在C++11中,可以在这个方法内定义一个局部静态变量,这样就能保证线程安全。如: class Singleton { public: static Singleton& getInstance() { static Singleton instance; return instance; } private: Singleton() {} Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; }; 这样写的话,无论多少线程调用getInstance(),instance只会被初始化一次。编译器会处理线程安全的问题,用户不需要自己加锁。这应该是最简的方法了。 但需要确认C++11的支持情况,用户的环境是否允许使用C++11或更高版本。如果用户只能用旧标准,可能需要其他方法,比如用互斥锁保护,但那样代码会更复杂。不过用户的问题中没有提到旧版本限制,所以假设可以使用C++11的话,这种方法是首选。 另外,需要注意的析构问题。静态变量的析构在程序结束时进行,但如果依赖其他静态变量,可能会有析构顺序的问题。不过一般情况下,如果不需要在析构时执行特别操作,这可能不是大问题。 总结一下,最简的方法是利用C++11的局部静态变量线程安全特性,实现模式,无需显式加锁,代码简洁高效。</think>在C++中实现线程安全的模式,最简的方法是结合**局部静态变量**与C++11的**线程安全静态初始化特性**。以下是分步说明: --- ### 步骤1:基础模式 ```cpp class Singleton { public: // 删除拷贝构造函数和赋值运算符 Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; // 获取唯一实的静态方法 static Singleton& getInstance() { static Singleton instance; // C++11保证线程安全 return instance; } private: Singleton() {} // 私有构造函数 }; ``` --- ### 步骤2:关键点解释 1. **局部静态变量** - C++11标准规定:局部静态变量的初始化是线程安全的,编译器会自动生成互斥锁保证只初始化一次。 - 无需手动加锁,代码简洁。 2. **删除拷贝和赋值** - 通过`delete`关键字禁用拷贝构造和赋值操作,防止意外复制实。 3. **私有构造函数** - 确保外部无法直接创建实,只能通过`getInstance()`获取。 --- ### 步骤3:验证线程安全性 多线程调用示: ```cpp #include <iostream> #include <thread> void threadFunc() { Singleton& s = Singleton::getInstance(); std::cout << &s << std::endl; } int main() { std::thread t1(threadFunc); std::thread t2(threadFunc); t1.join(); t2.join(); return 0; } ``` 输出结果会是相同的地址,证明所有线程访问的是同一个实。 --- ### 步骤4:对比其他方法 1. **双重检查锁定(DCLP)** ```cpp class Singleton { public: static Singleton* getInstance() { if (instance == nullptr) { // 第一次检查 std::lock_guard<std::mutex> lock(mutex); if (instance == nullptr) { // 第二次检查 instance = new Singleton(); } } return instance; } private: static Singleton* instance; static std::mutex mutex; }; ``` - **缺点**:需要手动管理内存,且C++11前可能因指令重排序导致问题。 2. **饿汉式** ```cpp class Singleton { public: static Singleton& getInstance() { return instance; } private: static Singleton instance; }; ``` - **缺点**:程序启动时即初始化,可能影响启动速度。 --- ### 总结 - **推荐方法**:C++11局部静态变量实现(代码量最少、无锁、线程安全)。 - **适用场景**:需要延迟初始化(懒加载)且兼容现代C++的项目。 - **注意事项**:确保编译器支持C++11或更高标准。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值