单例模式
来源:互联网 发布:大数据技术论文 编辑:程序博客网 时间:2024/06/04 19:03
最近写代码因为疏忽单例的判断,导致程序快速切换多次启动时出现了bug ,借这个机会梳理一下几种单例模式的构造方式。单例模式存在的意义是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
- 解决方法1(懒汉式)
一种实现方法是定义一个单例类,使用类的私有静态指针变量指向类的唯一实例,并用一个公有的静态方法获取该实例。单例模式通过类本身来管理其唯一实例,这种特性提供了解决问题的方法。唯一的实例是类的一个普通对象,但设计这个类时,让它只能创建一个实例并提供对此实例的全局访问。唯一实例类Singleton在静态成员函数中隐藏创建实例的操作。
class CSingleton { private: CSingleton() //构造函数是私有的 { } static CSingleton *m_pInstance; public: static CSingleton * GetInstance() { if(m_pInstance == NULL) //判断是否第一次调用 m_pInstance = new CSingleton(); return m_pInstance; } };
用户访问唯一实例的方法只有GetInstance()成员函数。如果不通过这个函数,任何创建实例的尝试都将失败,因为类的构造函数是私有的。GetInstance()使用懒惰初始化,也就是说它的返回值是当这个函数首次被访问时被创建的。所有GetInstance()之后的调用都返回相同实例的指针。
上面定义的CSingleton单例类有以下三个特征:(1 具有一个指向唯一实例的私有静态指针m_pInstance; (2 有一个可以获取这个唯一实例的公有函数,并且在需要的时候创建该实例; (3 构造函数私有,外界不可创建该类实例;
这种实现方式存在的一个问题是m_pInstance变量的释放问题,即实例的析构问题,虽然我们可以在程序结束时主动调用GetInstance()方法并对其返回的指针智行delete操作,但是这种方式不仅繁琐,而且调用者忘记的话,会引起很多的问题。
对于上面这种问题的一种解决方法是在该类中定义一个静态全局变量,我们知道,程序在结束的时候,系统会自动析构所有的全局变量。系统也会析构所有的类的静态成员变量,就像这些静态成员也是全局变量一样。我们可以在CSingleton单例类中定义一个这样的静态成员变量,在它的析构函数中删除单例类的实例。
class CSingleton { private: CSingleton() { } static CSingleton *m_pInstance; class CGarbo //它的唯一工作就是在析构函数中删除CSingleton的实例 { public: ~CGarbo() { if(CSingleton::m_pInstance) delete CSingleton::m_pInstance; } }; static CGarbo Garbo; //定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数 public: static CSingleton * GetInstance() { if(m_pInstance == NULL) //判断是否第一次调用 m_pInstance = new CSingleton(); return m_pInstance; } };
- 解决方法2(饿汉式)
如果你对上面添加在单例类内部添加一个类静态对象的方法不是很满意,还可以使用局部静态变量方法构造:
class CSingleton { private: CSingleton() //构造函数是私有的 { } public: static CSingleton & GetInstance() { static CSingleton instance; //局部静态变量 return instance; } };
用此种方法会出现类拷贝的问题,例如 Singleton singleton = Singleton :: GetInstance();这种调用方式编译器会为类生成一个默认的构造函数,来支持类的拷贝。这样的话就违背了单例的特性,所以我们可以稍微改一下,返回的是指针:
class CSingleton { private: CSingleton() //构造函数是私有的 { } public: static CSingleton * GetInstance() { static CSingleton instance; //局部静态变量 return &instance; } };
也可以不让编译器这么做,显示的声明类拷贝的构造函数,重载 = 操作符
class CSingleton { private: CSingleton() //构造函数是私有的 { } CSingleton(const CSingleton &); CSingleton & operator = (const CSingleton &); public: static CSingleton & GetInstance() { static CSingleton instance; //局部静态变量 return instance; } };
C++的单例模式与线程安全单例模式(懒汉/饿汉)
1 教科书里的单例模式
我们都很清楚一个简单的单例模式该怎样去实现:构造函数声明为private或protect防止被外部函数实例化,内部保存一个private static的类指针保存唯一的实例,实例的动作由一个public的类方法代劳,该方法也返回单例类唯一的实例。
上代码:
12345678910111213141516class
singleton
{
protected
:
singleton(){}
private
:
static
singleton* p;
public
:
static
singleton* instance();
};
singleton* singleton::p = NULL;
singleton* singleton::instance()
{
if
(p == NULL)
p =
new
singleton();
return
p;
}
这是一个很棒的实现,简单易懂。但这是一个完美的实现吗?不!该方法是线程不安全的,考虑两个线程同时首次调用instance方法且同时检测到p是NULL值,则两个线程会同时构造一个实例给p,这是严重的错误!同时,这也不是单例的唯一实现!
2 懒汉与饿汉
单例大约有两种实现方法:懒汉与饿汉。
- 懒汉:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化,所以上边的经典方法被归为懒汉实现;
- 饿汉:饿了肯定要饥不择食。所以在单例类定义的时候就进行实例化。
特点与选择:
- 由于要进行线程同步,所以在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。
- 在访问量较小时,采用懒汉实现。这是以时间换空间。
3 线程安全的懒汉实现
线程不安全,怎么办呢?最直观的方法:加锁。
方法1:加锁的经典懒汉实现:
class singleton{protected: singleton() { pthread_mutex_init(&mutex); }private: static singleton* p;public: static pthread_mutex_t mutex; static singleton* initance();};pthread_mutex_t singleton::mutex;singleton* singleton::p = NULL;singleton* singleton::initance(){ if (p == NULL) { pthread_mutex_lock(&mutex); if (p == NULL) p = new singleton(); pthread_mutex_unlock(&mutex); } return p;}
方法2:内部静态变量的懒汉实现
此方法也很容易实现,在instance函数里定义一个静态的实例,也可以保证拥有唯一实例,在返回时只需要返回其指针就可以了。推荐这种实现方法,真得非常简单。
class singleton{protected: singleton() { pthread_mutex_init(&mutex); }public: static pthread_mutex_t mutex; static singleton* initance(); int a;};pthread_mutex_t singleton::mutex;singleton* singleton::initance(){ pthread_mutex_lock(&mutex); static singleton obj; pthread_mutex_unlock(&mutex); return &obj;}
4 饿汉实现
为什么我不讲“线程安全的饿汉实现”?因为饿汉实现本来就是线程安全的,不用加锁。为啥?自己想!
123456789101112131415class
singleton
{
protected
:
singleton()
{}
private
:
static
singleton* p;
public
:
static
singleton* initance();
};
singleton* singleton::p =
new
singleton;
singleton* singleton::initance()
{
return
p;
}
是不是特别简单呢?
以空间换时间,你说简单不简单?
面试的时候,线程安全的单例模式怎么写?肯定怎么简单怎么写呀!饿汉模式反而最懒[正经脸]!
单例模式中,饿汉式和懒汉式有什么区别?各适合用在哪里?为什么说推荐用饿汉模式?
饿汉式:
public class Singleton{
private static Singleton singleton = new Singleton ();
private Singleton (){}
public static Singleton getInstance(){return singletion;}
}
懒汉式:
public class Singleton{
private static Singleton singleton = null;
public static synchronized synchronized getInstance(){
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
比较:
饿汉式是线程安全的,在类创建的同时就已经创建好一个静态的对象供系统使用,以后不在改变
懒汉式如果在创建实例对象时不加上synchronized则会导致对对象的访问不是线程安全的
推荐使用第一种 从实现方式来讲他们最大的区别就是懒汉式是延时加载,
他是在需要的时候才创建对象,而饿汉式在虚拟机启动的时候就会创建,
饿汉式无需关注多线程问题、写法简单明了、能用则用。但是它是加载类时创建实例(上面有个朋友写错了)、所以如果是一个工厂模式、缓存了很多实例、那么就得考虑效率问题,因为这个类一加载则把所有实例不管用不用一块创建。
懒汉式的优点是延时加载、缺点是应该用同步(想改进的话现在还是不可能,比如double-check)、其实也可以不用同步、看你的需求了,多创建一两个无引用的废对象其实也没什么大不了。
阅读全文 0 0
- 单例、单例模式
- 单例模式-多线程单例模式
- 单件模式(单例模式)
- 设计模式------单例模式
- 设计模式------单例模式
- 设计模式-单例模式
- 设计模式 - 单例模式
- 设计模式---单例模式
- 设计模式---单例模式
- PHP模式-单例模式
- 【设计模式】单例模式
- 设计模式-单例模式
- 设计模式----单例模式
- 设计模式--单例模式
- 设计模式-单例模式
- 单例模式(单子模式)
- 设计模式-单例模式
- [设计模式] 单例模式
- API安全设计
- 隔行扫描(interlaced)与逐行扫描(progressive)的图像对比
- 给大家上点儿干货
- 7台主机模拟DNS架构
- Thinking In Java 之 多线程 1
- 单例模式
- Hibernate监听用户操作日志
- 冒泡排序 bubble sort
- JAVA设计模式 单例模式
- jQuery动态效果
- Packets
- 结合FME利用倾斜三维模型数据成果生成DSM等数据产品
- centos postgresql
- Python 文件和目录管理(os)
原创粉丝点击
热门IT博客
热门问题
老师的惩罚
人脸识别
我在镇武司摸鱼那些年
重生之率土为王
我在大康的咸鱼生活
盘龙之生命进化
天生仙种
凡人之先天五行
春回大明朝
姑娘不必设防,我是瞎子
华为密保柜忘记怎么办
窗台缝漏水怎么办
淘宝橱柜不够怎么办
衣柜不靠墙怎么办
衣柜靠墙很潮怎么办
老保险柜打不开怎么办
水箱水压不够怎么办
led施工现场照明怎么办
指甲油瓶盖打不开怎么办
搬运费没有发票怎么办
cpu使用过高怎么办
lte丢包率高怎么办
加速器丢包率高怎么办?
水泥地起砂怎么办
vivox7开机死机怎么办
宠物狗病了怎么办
床太高了怎么办
床尾有两个柱子怎么办
12岁感统失调怎么办
ps源文件太大怎么办
老年人前列腺炎怎么办
婴儿发热怎么办
小孩寒包火咳嗽怎么办
智齿有臭味怎么办
口腔臭味怎么办
牙龈结石怎么办
感觉口干口臭怎么办
卖厨卫电器怎么办质检
保鲜柜不凉怎么办
公司工商异常怎么办
夏天天气干燥怎么办
鞋柜臭味怎么办
鞋柜有臭味怎么办
防潮箱太干怎么办
潜水气瓶爆了怎么办
净水器浪费水怎么办
贵宾不吃东西怎么办
冲水箱漏水怎么办
防火泥太硬怎么办
蹲坑水箱漏水怎么办
老暖气片漏水怎么办