再谈单例模式之饿汉与懒汉

2017-07-20 20:08 阅读 681 次 评论 0 条

前面对单例模式已经做了一定的分析,潜移默化的提出了单例的饿汉、懒汉模式,但并没有针对两种模式做进一步的分析,本篇将对两个负能量设计模式作以深入。

饿汉式单例

饿汉单例:在类装载时完成了初始化,静态成员对象创建成功,类加载速度相比懒汉慢,但获取对象的速度快,是一种典型的以空间换时间的做法。

设计思想:私有构造函数,在类中创建一个静态成员指针,该指针指向类的一个对象,通过一个public的静态成员函数,返回该对象的指针。

从运行结果可以看出,静态成员对象的创建是在main函数之前。此外,当创建一个对象时,再次创建对象时地址一致,表明单例模式返回的是同一个对象。

饿汉式是线程安全的,保证了只会装载一次,在装载类的时候是不会发生并发问题的。

但是,饿汉模式不管你用不用这个对象,它都会先创建一个出来,试想如果一直不使用这个对象,就会浪费内存空间,这也是饿汉模式的一个缺点。

懒汉式单例

懒汉模式:在类装载时不会创建对象的实例,而是尽可能晚的创建实例,类似于之前的Copy on write、晚绑定等。因此类的加载速度很快,但运行时获取对象会稍慢,是一种典型的以时间换空间的做法。

设计思想:私有构造函数,在类中创建一个静态成员指针,该指针指向类的一个对象,通过一个public的静态成员函数,返回该对象的指针,创建时机按需分配。

上述结果可以看出,首次创建对象成功后,再次创建创建就会失败,并且两个对象的地址是一致的,也就保证了全局只有一个实例。

但是很可惜,从线程安全的角度来讲,不加同步的懒汉式是线程不安全的,当有两个线程同时调用GetInstance时,可能会导致并发问题。

解决懒汉式单例的线程安全问题

双重检查加锁:用加锁机制来确保多线程下只创建一个实例,并且用两个if判断来提高效率。使用所谓的双检锁机制,因为进行一次加锁和解锁是需要付出对应的代价的,而进行的两次判断,就可以避免多次加锁与解锁的操作,同时也保证了线程的安全性。

通过双重检查,避免了高并发环境下每次获取实例加、解锁的操作,是一种高效率的方式。除此之外,通过内存栅栏也避免了线程安全的问题,具体见下图伪代码:

实例是否应该销毁

在实际项目开发中,尤其是客户端开发,其实不在乎这个实例的销毁。因为全局仅有一个实例,全局都在使用,它的生命周期伴随着软件的生命周期,在软件结束时,也就自然而然结束了,也就没有所谓的内存泄漏可言。

但是有些情况需要进行实例销毁的:比如 在类中,有一些文件锁、文件句柄、数据库连接等,这些随着程序的关闭不会立即关闭的资源,必须在程序关闭前,进行手动释放内存。

单例模式的优缺点

单例模式的优点:

① 提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它。

② 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。

③ 允许可变数目的实例,基于单例模式我们可以扩展,使用与单例控制相似的方法来获得指定个数的对象实例。(即单例类内有多个静态对象指针成员,每次当单例类被引用时随机分配一个实例对象);

单例模式的缺点:

① 因为单例模式没有抽象层,所以单例类的扩展有很大的困难。

② 单例类的职责过重,即是工厂角色,提供了工厂的方法,同时又充当了产品的角色。

③ 滥用单例类会带来一些负面问题。

版权声明:本文著作权归原作者所有,欢迎分享本文,谢谢支持!
转载请注明:再谈单例模式之饿汉与懒汉 | 术与道的分享
分类:编程素养 标签:, ,
1024do.com导航_术与道导航平台

发表评论


表情