目录
[3.1懒汉式(Lazy Initialization,线程不安全)](#3.1懒汉式(Lazy Initialization,线程不安全))
[3.3双重检查锁(Double-Checked Locking)](#3.3双重检查锁(Double-Checked Locking))
[3.4饿汉式(Eager Initialization)](#3.4饿汉式(Eager Initialization))
一、什么是单例模式?
在软件开发中,某些对象只需要一个实例,且该实例应该在整个系统范围内被共享。这种需求催生了 单例模式(Singleton Pattern) 。单例模式是一种 创建型设计模式,它确保一个类在整个应用程序的生命周期中只有一个实例,并提供一个全局访问点。
1.1单例模式的特点:
-
唯一实例:确保一个类只有一个对象。
-
全局访问:提供一个公共访问点来获取实例。
-
延迟实例化(可选):在首次使用时创建实例,避免不必要的资源浪费。
二、什么时候使用单例模式?
2.1适用场景
-
配置管理:应用程序的配置信息通常应该是唯一的,例如日志管理、数据库连接池等。
-
资源访问控制:如打印机管理器、线程池、缓存管理等。
-
全局状态管理:如游戏引擎中的场景管理、设备管理等。
2.2实际应用案例
案例1:日志管理
在应用程序中,日志系统通常是全局的,多个组件都需要访问它。如果每次记录日志都创建一个新对象,会导致资源浪费。使用单例模式可以确保日志管理器是唯一的,并可在整个程序中共享。
案例2:数据库连接池
在数据库操作中,创建和关闭数据库连接非常耗时。使用单例模式可以维护一个全局的数据库连接池,提高性能。
三、单例模式的不同实现方式
3.1懒汉式(Lazy Initialization,线程不安全)
懒汉式,在首次访问时创建实例,不适用于多线程环境,在多线程环境下可能会创建多个实例。
cpp
class Singleton {
private:
static Singleton* instance;
Singleton() {} // 构造函数私有化
public:
static Singleton* getInstance() {
if (instance == nullptr) { // 仅在首次调用时创建
instance = new Singleton();
}
return instance;
}
};
// 初始化静态成员变量
Singleton* Singleton::instance = nullptr;
3.2线程安全的懒汉式
使用互斥锁确保线程安全,但会影响性能,每次访问实例都会加锁。
cpp
#include <mutex>
class Singleton {
private:
static Singleton* instance;
static std::mutex mtx;
Singleton() {}
public:
static Singleton* getInstance() {
std::lock_guard<std::mutex> lock(mtx); // 加锁
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
};
// 初始化静态成员
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;
3.3双重检查锁(Double-Checked Locking)
改进线程安全的懒汉式,通过双重检查只在必要时加锁,提高性能,但在某些架构下,可能会遇到指令重排导致未正确初始化实例。
cpp
class Singleton {
private:
static Singleton* instance;
static std::mutex mtx;
Singleton() {}
public:
static Singleton* getInstance() {
if (instance == nullptr) { // 第一次检查
std::lock_guard<std::mutex> lock(mtx);
if (instance == nullptr) { // 第二次检查
instance = new Singleton();
}
}
return instance;
}
};
// 初始化
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;
3.4饿汉式(Eager Initialization)
在程序启动时就创建实例,避免了线程同步问题,但可能导致资源浪费。线程安全无需加锁。
cpp
class Singleton {
private:
static Singleton* instance;
Singleton() {}
public:
static Singleton* getInstance() {
return instance;
}
};
// 直接初始化
Singleton* Singleton::instance = new Singleton();
3.5C++11静态局部变量
C++11保证了函数内静态变量的初始化是线程安全的,可以用它简洁高效地实现单例模式。自动管理生命周期。
cpp
class Singleton {
private:
Singleton() {}
public:
static Singleton& getInstance() {
static Singleton instance; // 静态局部变量,线程安全
return instance;
}
};
四、注意事项
4.1线程安全
多线程环境下必须使用线程安全的的单例实现方式,如双重检查锁或C++11静态局部变量。
4.2内存管理
- 手动管理的单例(懒汉式)可能会导致内存泄露,应在程序退出时释放。
- 饿汉式单例在程序结束时由系统自动释放。
4.3禁止拷贝和赋值
为了防止创建多个实例,单例类应删除拷贝构造函数和赋值运算符
cpp
class Singleton {
private:
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton& getInstance() {
static Singleton instance;
return instance;
}
};
4.4适用性
不要滥用单例,否则会导致模块间高度耦合,难以测试和维护。
适用于需要唯一实例的场景,如日志管理、线程池、数据库连接池等。