单例模式概念
单例模式:保证一个类仅有一个实例化对象,并且提供一个访问它的全局访问点。
就是说无论怎么去构造对象,该类只能实例化一个对象,并且是一个全局访问的。因此可以根据它的概念去进行实现
实现逻辑:
1.构造函数私有,这样用户就不能构造出对象
2.定义唯一一个静态对象
3.实现一个公共接口访问静态对象
实现方式:饿汉与懒汉
饿汉单例模式:程序启动时就实例化唯一一个对象,在类定义时就创建一个唯一对象。
懒汉单例模式:在第一次调用使用对象时在进行创建唯一的实例化对象,实现延迟加载效果
饿汉单例模式
代码
class Singleton
{
public:
static Singleton *getInstance()
{
return &instance;
}
private:
Singleton() { cout << "Singleton()" << endl; }
Singleton(const Singleton &instance) { cout << "Singleton(const Singleton &)" << endl; }
static Singleton instance;
};
Singleton Singleton::instance;
int main()
{
Singleton *p1 = Singleton::getInstance();
Singleton *p2 = Singleton::getInstance();
Singleton *p3 = Singleton::getInstance();
cout << p1 << " " << p2 << " " << p3 << endl;
return 0;
}
从上面结果可以看到申请的三个对象都是同一个对象
懒汉单例模式
普通的懒汉单例模式
class Singleton
{
public:
static Singleton *getInstance()
{
if (instance == nullptr)
{
instance = new Singleton();
}
return instance;
}
private:
static Singleton *instance;
Singleton() { cout << "Singleton()" << endl; }
Singleton(const Singleton &instance) { cout << "Singleton(const Singleton &)" << endl; }
~Singleton() { cout << "~Singleton()" << endl; }
class Release
{
public:
~Release()
{
if (instance != nullptr)
{
delete instance;
instance = nullptr;
}
}
};
static Release del;
};
Singleton *Singleton::instance = nullptr;
Singleton::Release Singleton::del;
int main()
{
Singleton *p1 = Singleton::getInstance();
Singleton *p2 = Singleton::getInstance();
cout << p1 << " " << p2 << endl;
return 0;
}
可以看到也只能申请到一个对象
由于在getInstance()是一个不可重入函数 if (instance == nullptr) { instance = new Singleton(); } 是一个非原子操作,因此在多线程环境下是一个非线程安全的单例模式
线程安全的懒汉单例模式
使用互斥锁来保证线程安全
class Singleton
{
public:
static Singleton *getInstance()
{
pthread_mutex_lock(&_mutex);
if (instance == nullptr)
{
instance = new Singleton();
}
pthread_mutex_unlock(&_mutex);
return instance;
}
private:
Singleton()
{
cout << "Singleton()" << endl;
}
Singleton(const Singleton &instance)
{
cout << "Singleton(const Singleton&)" << endl;
}
~Singleton()
{
pthread_mutex_destroy(&_mutex);
cout << "~Singleton()" << endl;
}
class Release
{
public:
~Release()
{
if (instance != nullptr)
{
delete instance;
instance = nullptr;
}
}
};
static Release rel;
static Singleton *instance;
static pthread_mutex_t _mutex;
};
Singleton *Singleton::instance = nullptr;
pthread_mutex_t Singleton::_mutex = PTHREAD_MUTEX_INITIALIZER;
Singleton::Release Singleton::rel;
int main()
{
Singleton *p1 = Singleton::getInstance();
Singleton *p2 = Singleton::getInstance();
cout << p1 << " " << p2 << endl;
return 0;
}
优化:双重判断+锁
上述代码由于使用锁进行了频繁的加锁解锁操作,导致效率不高,我们只需要在第一次调用时进行加锁解锁操作就可以了,因此需要进行优化。
针对getInstace()进行修改
static Singleton *getInstance()
{
if (instance == nullptr)
{
pthread_mutex_lock(&_mutex);
/*
此时需要在进行一次判空
原因:有多个线程在第一次判断空时进入了第一个if语句
此时如果不在进行判空 在解锁后会有线程再次去进行new对象
*/
if (instance == nullptr)
{
instance = new Singleton();
}
pthread_mutex_unlock(&_mutex);
}
return instance;
}
问题:在外面已经进行一次instance判空 为什么加锁后还要进行判空?
基于OOP思想的懒汉单例模式
我们将锁进行封装,这样就会对代码进行解耦,使得耦合性降低
class Cmutex
{
public:
Cmutex() { pthread_mutex_init(&_mutex, nullptr); }
~Cmutex() { pthread_mutex_destroy(&_mutex); }
void lock() { pthread_mutex_lock(&_mutex); }
void unlock() { pthread_mutex_unlock(&_mutex); }
private:
pthread_mutex_t _mutex;
};
class Singleton
{
public:
static Singleton *getInstance()
{
if (instance == nullptr)
{
_mutex.lock();
if (instance == nullptr)
{
instance = new Singleton();
}
_mutex.unlock();
}
return instance;
}
private:
Singleton() { cout << "Singleton()" << endl; }
Singleton(const Singleton &instance) { cout << "Singleton(const Singleton &)" << endl; }
~Singleton() { cout << "~Singleton()" << endl; }
//自动析构instance类
class Release
{
public:
~Release()
{
if (instance != nullptr)
{
delete instance;
instance = nullptr;
}
}
};
static Release rel;
static Singleton *instance;
static Cmutex _mutex;
};
Singleton *Singleton::instance = nullptr;
Singleton::Release Singleton::rel;
Cmutex Singleton::_mutex;
int main()
{
Singleton *p1 = Singleton::getInstance();
Singleton *p2 = Singleton::getInstance();
cout << p1 << " " << p2 << endl;
return 0;
}
使用内部静态变量保证线程安全
class Singleton
{
public:
static Singleton* getInstance()
{
static Singleton single;
return &single;
}
private:
static Singleton *single;
Singleton() { cout << "Singleton()" << endl; }
Singleton(const Singleton &instance) { cout << "Singleton(const Singleton &)" << endl; }
~Singleton() { cout << "~Singleton()" << endl; }
};
int main()
{
Singleton *p1 = Singleton::getInstance();
Singleton *p2 = Singleton::getInstance();
cout << p1 << " " << p2 << endl;
return 0;
}
使用GDB调试查看汇编代码可以看到对静态成员进行初始化时,编译器进行了加锁解锁的保护,因此他是一个线程安全的单例模式