在设计模式中,单例模式是最常见的一种模式。下面是本人学习单例模式的一些总结。用java和C++两种语言实现单例模式
定义
确保一个类只有一个实例,并提供一个全局访问点。
实现方法
如何在实际软件中,做到类只有一个实例呢。最简单的方法,就是使类的构造函数私有,这样类的使用者,就不能创建类的实例,然后,再提供统一的获取类实例的方法。
下面就给出Java和C++两种具体语言的实现。
JAVA实现方法
静态变量
Singleton最简单的方法,就是直接声明一个static变量,并且进行初始化,这样类在加载的时候,就会初始化实例。
public class Singleton
{
private volatile static Singleton singleton = new Singleton();
private Singleton() { }
public static Singleton getInstance() {
return singleton;
}
}
双检锁
使用双检索的方式,必须添加volatile关键字,
因为JVM的重排序,会导致未被初始化的实例.
public class Singleton
{
private volatile static Singleton singleton = null;
private Singleton(){}
public static Singleton getInstance(){
if (singleton== null) {
synchronized (Singleton.class) {
if (singleton== null) {
//没有volatile关键字会因为重排序,导致singleton!=null,而实例未被成功初始化
singleton= new Singleton();
}
}
}
return singleton;
}
}
enum实现
使用jdk1.5之后的enum可以很方便实现单例。
public enum Singleton {
INSTANCE;
}
C++实现方法
C++和java差不多,但C++中的构造函数种类比较多,有普通构造函数,有拷贝构造函数。为了避免实例被外部delete,可以将类的析构函数也私有,这样在外部调用delete的时候,也会有编译错误。
静态局部变量
C++最简单的方式,就是使用静态局部变量。在第一次获取instance的时候,会进行初始化。
#include <iostream>
using namespace std;
class Singleton{
public:
static Singleton& getInstance(){
static Singleton instance;
return instance;
}
void print(){
cout << "Hello, my data:" << data << endl;
}
void add(int i){
data += i;
}
private:
Singleton(){
data = 0;
};
Singleton(Singleton &){}
~Singleton(){}
int data;
};
int main(int argc, const char* argv[]) {
Singleton & s1 = Singleton::getInstance();
Singleton & s2 = Singleton::getInstance();
s1.print();
s2.add(2);
s1.print();
}
但这种方式,在C++0x之前,是线程不安全的,C++0x之后,局部静态变量是线程安全的。
静态变量
C++中,为了避免C++中局部变量的线程安全问题,可以将局部静态变量放到类的静态变量中。
#include <iostream>
using namespace std;
class Singleton{
public:
static Singleton& getInstance(){
return instance;
}
void print(){
cout << "Hello, my data:" << data << endl;
}
void add(int i){
data += i;
}
private:
static Singleton instance;
private:
Singleton(){
data = 0;
};
Singleton(Singleton &){}
~Singleton(){}
int data;
};
Singleton Singleton::instance ;
int main() {
Singleton & s1 = Singleton::getInstance();
Singleton & s2 = Singleton::getInstance();
s1.print();
s2.add(2);
s1.print();
return 0;
}
这种实现,有个问题,单例的初始化,是类的静态初始化时完成的,无法控制两个类的初始化顺序。如果有两个单例相互依赖,则会有问题。
双检锁
为了使用延迟初始化,同时解决线程安全问题,可以使用双检锁的方式,先判断实例是否为空,如果为空,则加锁,加锁后,再进行一次为空判断。这样,只有在单例被初始化的时候,才会有加锁的性能问题。
#include <iostream>
#include <mutex>
using namespace std;
class Singleton{
public:
static Singleton& getInstance(){
if (instance == NULL){
m.lock();
if (instance == NULL){
instance = new Singleton();
}
m.unlock();
}
return *instance;
}
void print(){
cout << "Hello, my data:" << data << endl;
}
void add(int i){
data += i;
}
private:
static Singleton* instance;// = new Singleton();
static mutex m;
private:
Singleton(){
data = 0;
};
Singleton(Singleton &){}
~Singleton(){}
int data;
};
Singleton* Singleton::instance = NULL;
mutex Singleton::m;
int main(int argc, const char* argv[]) {
Singleton & s1 = Singleton::getInstance();
Singleton & s2 = Singleton::getInstance();
s1.print();
s2.add(2);
s1.print();
return 0;
}
单例角
如果有多个单例,都想使用双检锁的方法,
利用C++的模板和friend友元功能,可以快速产生单例类
#include <iostream>
#include <mutex>
using namespace std;
template<class T>
class Singleton{
public:
static T& getInstance(){
if (nullptr == instance){
m.lock();
if (nullptr == instance){
instance = new T();
}
m.unlock();
}
return *instance;
}
private:
static T* instance;
static mutex m;
protected:
Singleton(){};
Singleton(Singleton &){}
~Singleton(){}
};
template<class T>
T* Singleton<T>::instance = nullptr;
template<class T>
mutex Singleton<T>::m;
class MyClass : public Singleton<MyClass>{
friend class Singleton<MyClass>;
public:
void print(){
cout << "Hello, my data:" << data << endl;
}
void add(int i){
data += i;
}
private:
MyClass():data(0){}
MyClass(MyClass &){}
~MyClass(){}
int data;
};
int main(int argc, const char* argv[]) {
MyClass & s1 = MyClass::getInstance();
MyClass & s2 = MyClass::getInstance();
s1.print();
s2.add(2);
s1.print();
return 0;
}
应用场景
在现实中,有很多地方都是需要用到单例模式的,比如全局配置信息,数据库连接池