引言
前段时间面试时遇到一个问题,静态变量是否为线程安全的。静态变量在编译时进入main()函数前便进行初始化,其生命周期伴随着整个程序的生命周期,在程序结束时才会释放内存。对于静态实例变量,其在构造的时候是线程安全的,构造过程中第一个线程执行构造,其他线程会阻塞在构造的过程,然后构造完毕后该对象内部的静态变量是非线程安全的,因为其共享的是一份内存。
因此,在设计工具类的时候,对于没有使用静态变量的静态工具类方法,是不需要加锁的。在使用单例模式做工具类的时候,静态方法需要被加锁,这是因为所有的线程虽然有自己的方法栈,但是在方法栈中操作的是同一个对象的实体,需要进行加锁同步。
单例模式
现在回想起来,其实面试官真正想问的问题是如何设计一个单例模式类。
单例模式是指确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
单例模式常用于系统的日志输出等工具类的实现。其特点如下:
优点:仅有一个实例,减少内存开销;避免对一个资源的多重占用;设置全局访问点,优化和贡献资源;
缺点:扩展性差;不利于测试;与单一职责原则有冲突,单例模式要把单例和业务逻辑融合在一个类中。
懒汉模式
懒汉模式是指只有在第一次用到类实例的时候才会去实例化。最普通的懒汉模式如下:
class Singleton
{
private:
Singleton() {}
static Singleton* m_instance;
public:
static Singleton* getInstance();
};
Singleton* Singleton::m_instance = NULL;
Singleton* Singleton::getInstance()
{
if (nullptr == m_instance)
{
m_instance = new Singleton();
}
return m_instance;
}
懒汉模式保证只有一个实例是通过第一次访问getInstance()这个函数时实现的,只有第一次访问时才会构建对象,否则仅返回相同实例的指针。但是其会存在两个问题:多个线程同时第一次访问getInstance时会同时构造一个实例给m_instance造成错误;new出来的东西没有进行析构,导致内存泄漏。
在C++11中,我们可以借助call_once和unique_ptr来实现线程安全的单例模式,此处搬运别人的代码如下:
class Singleton {
public:
static Singleton& GetInstance() {
static std::once_flag s_flag;
std::call_once(s_flag, [&]() {
m_instance.reset(new Singleton);
});
return *m_instance;
}
~Singleton() = default;
private:
Singleton() = default;// ctor hidden
Singleton(const Singleton&) = delete; // copy ctor hidden
Singleton& operator=(const Singleton&) = delete; // assign op. hidden
private:
static std::unique_ptr<Singleton> m_instance;
};
其中unique_ptr智能指针可禁止拷贝和赋值,由于智能指针的特性可以避免了内存泄漏,call_once函数可避免getInstance在第一次时多个线程同时访问时出现的同时构造的问题。
由此看来,C++11的一些特性太强大了,怪不得好几个面试官都要问C++11的问题呢。
饿汉模式
饿汉模式是指单例类定义的时候就进行实例化,饥不择食。实现代码如下:
class Singleton
{
private:
Singleton() {}
~Singleton()
{
if (nullptr != m_instance)
{
delete m_instance;
m_instance = nullptr;
}
}
static Singleton* m_instance;
public:
static Singleton* getInstance();
};
Singleton* Singleton::m_instance = new Singleton(); //static members initialize out of class
Singleton* Singleton::getInstance()
{
return m_instance;
}
参考链接:
https://blog.youkuaiyun.com/u011726005/article/details/82356538