面试题答案
一键面试问题分析
- 竞争条件:多个线程同时尝试初始化抽象类的静态成员变量时,可能会导致数据竞争。例如,两个线程同时对同一个静态变量进行写操作,最终结果可能取决于线程调度顺序,导致不可预测的行为。
- 初始化顺序问题:如果抽象类的静态成员变量之间存在依赖关系,在多线程环境下,初始化顺序可能混乱,导致某些变量在使用时未正确初始化。
解决方案
1. 使用静态局部变量(C++ 中的 Meyers' Singleton 模式变体)
实现原理:在函数内部定义静态局部变量,C++ 保证该变量在首次使用时进行初始化,并且这种初始化是线程安全的(C++11 及以后)。 适用场景:适用于需要延迟初始化且只需要一个实例的情况,比如单例模式的实现。 代码示例(C++):
#include <iostream>
class AbstractBase {
public:
static AbstractBase& getInstance() {
static AbstractBase instance;
return instance;
}
virtual void someAbstractMethod() = 0;
protected:
AbstractBase() = default;
~AbstractBase() = default;
// 防止拷贝和赋值
AbstractBase(const AbstractBase&) = delete;
AbstractBase& operator=(const AbstractBase&) = delete;
};
class ConcreteClass : public AbstractBase {
public:
void someAbstractMethod() override {
std::cout << "Concrete implementation of abstract method" << std::endl;
}
};
在这个示例中,AbstractBase::getInstance
函数中的 static AbstractBase instance
保证了 AbstractBase
的实例(这里可以理解为类似静态成员变量的延迟初始化)是线程安全的。
2. 使用互斥锁(Mutex)
实现原理:通过互斥锁来保护对静态成员变量的初始化操作,确保同一时间只有一个线程可以执行初始化代码。 适用场景:适用于多种需要保证初始化原子性的场景,不局限于单例模式。 代码示例(C++):
#include <iostream>
#include <mutex>
class AbstractBase {
public:
static AbstractBase& getInstance();
virtual void someAbstractMethod() = 0;
protected:
AbstractBase() = default;
~AbstractBase() = default;
// 防止拷贝和赋值
AbstractBase(const AbstractBase&) = delete;
AbstractBase& operator=(const AbstractBase&) = delete;
private:
static AbstractBase* instance;
static std::mutex mtx;
};
AbstractBase* AbstractBase::instance = nullptr;
std::mutex AbstractBase::mtx;
AbstractBase& AbstractBase::getInstance() {
if (instance == nullptr) {
std::lock_guard<std::mutex> lock(mtx);
if (instance == nullptr) {
instance = new AbstractBase();
}
}
return *instance;
}
class ConcreteClass : public AbstractBase {
public:
void someAbstractMethod() override {
std::cout << "Concrete implementation of abstract method" << std::endl;
}
};
在这个代码中,std::mutex mtx
用于保护对 instance
的初始化,确保多线程环境下初始化的正确性。double - check
机制(if (instance == nullptr)
两次判断)可以在一定程度上提高性能,减少每次获取实例时都加锁的开销。