Singleton模式:只能生成一个实例的类是实现了Singleton模式的类型
快加载:未调用创建对象的函数时,已经为对象分配好内存,以及初始化。
class Singleton
{
public:
static Singleton* getmySingleton()
{
return &mySingleton;
}
private:
Singleton() { };
static Singleton mySingleton;
};
Singleton Singleton::mySingleton;
快加载是线程安全的,因为在main函数之前,要进行对static变量进行初始化,因此多线程不论怎样调度都是返回的是同一个对象。
懒加载:在调用创建对象的时候,才给开辟内存,构造对象
class Singleton
{
public:
static Singleton* getSingleton()
{
if(mySingleton == NULL)
{
pthread_mutex_lock(&mutex);
if(mySingleton == NULL)
{
mySingleton = new Singleton();
}
pthread_mutex_unlock(&mutex);
}
return &mutex;
}
private:
Singleton() { }
static pthread_mutex mutex;
static volatile Singleton* mySingleton;
~Singleton() { pthread_mutex_destroy(&mutex,NULL); delete mySingleton}
};
pthread_mutex Singleton::mutex = PTHREAD_MUTEX_INITIALIZER;
Singleton* Singleton::mySingleton = NULL;
在这里解释两个东西:
一、进行双重锁机制的原因
当线程1运行到加锁完成时,线程2现在抢占cpu也要进行加锁,然后进行阻塞,进而1线程完成了对象的创建,并且释放了锁。现在线程2现在立即加锁成功,进行对象的创建,则出现错误,对对象指针进行了二次new。二次锁则很有效率的解决了这个问题。
二、volatiled的作用
如果cpu将mySingleton读取到高速缓冲区当中,当线程1对mySingleton进行new之后,还未写回内存,只是写入到了缓冲区当中。那么线程2读取的mySingleton 为NULL,继续对mySingleton 进行new,则会出错。
而volatile声明的变量,当cpu要读取或者写入mySingleton 时都会直接从内存上进行访问,不会对mySingleton 进行缓存。
可以对锁进行封装,提高代码的便捷性
class CMutex //对锁进行封装
{
public:
CMutex()
{
pthread_mutex_init(&mutex,NULL);
}
~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(mpSingleTon == NULL)
{
mutex.lock(); //将加锁解锁放在if语句内是为了保证在单线程下程序执
if(mpSingleTon == NULL)?????????? 行的效率。
mpSingleTon = new SingleTon ();
mutex.unlock();
}
return &SingleTon
}
private:
SingleTon(){};
~SingleTon(){};
static CMutex mutex;
static volatile SingleTon *mpSingleTon; //告诉操作系统 mpSingleTon是一个异变的变量。操作时直接
从内存上(read/write)
}
CMutex SingleTon::mutex;
SingleTon * SingleTon ::mpSingleTon = NULL;