一、懒汉式
(一)线程不安全的懒汉式
特点: 时间换空间
懒汉:在第一次用到类实例的时候,实例化对象。
(a)构造函数声明为private或protect防止被外部函数实例化。
(b)内部保存一个private static的指针ptr,保存全局唯一的实例对象。
(c)实例化的动作由一个public的方法代劳,该方法返回单例类唯一的实例对象。
问题: 线程不安全。
当两个线程同时首次调用Instance方法,
并且同时检测到ptr是nullptr值,则两个线程会同时构造一个实例给ptr。
class Singleton {
public:
static Singleton* Instance() {
if (ptr == nullptr) {
ptr = new Singleton;
}
return ptr;
}
private:
static Singleton* ptr;
private:
Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
Singleton* Singleton::ptr = nullptr;
(二)线程不安全的懒汉式–加锁
方法: Double-Checked Locking Pattern (DCLP)。
问题:
1.线程不安全。
第一次check没有加锁,在多线程情况下,存在线程不安全问题。
2.资源需要手动释放
需要手动调用Destroy(),释放单例模式管理的内部内存资源。
class Singleton {
public:
static Singleton* Instance() {
if (ptr == nullptr) {
pthread_mutex_lock(&mutex);
if (ptr == nullptr) {
ptr = new Singleton;
}
pthread_mutex_unlock(&mutex);
}
return ptr;
}
void Destroy() {
if (ptr != nullptr) {
delete ptr;
ptr = nullptr;
}
}
private:
static Singleton* ptr;
static pthread_mutex_t mutex;
private:
Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
Singleton* Singleton::ptr = nullptr;
(三)线程安全的懒汉式–局部静态变量
在Instance方法里定义一个静态实例,可以保证拥有唯一实例,
在返回时只需要返回其指针就可以了。
推荐这种实现方法。
class Singleton {
public:
static Singleton* Instance() {
static Singleton obj;
return &obj;
}
private:
Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
(四)线程安全的懒汉式–std::call_once
C++11提供一种方法,可以线程安全地只调用一次函数。
使用std::call_once和std::once_flag。
std::call_once是一种lazy load机制。
class Singleton {
public:
virtual ~Singleton(void) = default;
static std::shared_ptr<Singleton> Instance() {
static std::once_flag onceflag;
static std::shared_ptr<Singleton> ptr = nullptr;
std::call_once(onceflag, [&]() {
(new Singleton());
});
return ptr;
}
private:
Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
二、饿汉式
特点:空间换时间
饿汉:在单例类定义的时候就进行实例化对象。
(a)构造函数声明为private或protect防止被外部函数实例化。
(b)内部保存一个private static的指针ptr,保存全局唯一的实例对象。
(c)实例化的动作由一个public的方法代劳,该方法返回单例类唯一的实例对象。
(d)线程安全。
class Singleton {
public:
static Singleton* Instance() {
return ptr;
}
private:
static Singleton* ptr;
private:
Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
Singleton* Singleton::ptr = new Singleton;
特点与选择:
在访问量较小时,采用懒汉实现,这是以时间换空间。
在访问量比较大,采用饿汉实现,这是以空间换时间。