设计模式——单例模式(学习笔记)

原创 2012年03月25日 19:14:53

       最近在学习设计模式,并决定把学习的东西写成笔记,也算是心得了吧。

——前言

定义:单例模式就是当你希望在你的工程中有个只会实例化一次的类(或者说只有一个对象)时,需要使用的设计模式。既然使用这种模式的类只可能有一个对象,自然要提供一个随时访问的全局访问点。

    用更专业点的话说,单例模式确保类仅有一个实例,并该类提供了一个全局访问点。

实现:

单线程

对于一个单线程的程序来说,实现单例模式是很容易的,实现的技巧就是,将构造器设置为私有的(没学单例之前我都不知道可以这个样子),这样该类就只能在本类中实例化。再定义一个静态方法允许在其他类中获取该实例。而根据实例化的方式,可以分为两种,主动实例化和被动实例化(对应的也称之为饿汉式和懒汉式,个人更喜欢前面那种的名字);

主动实例化(饿汉式):被加载时就将自己实例化。为静态实例化方式。它在静态初始化期间或在类的构造器中分配变量。类一加载就实例化,所以要提前占用系统资源。当这个类初始化要占用很多资源时,这么写就有点囧了。java源码:

publicclass Singleton {

//唯一私有静态引用,类一加载就实例化

privatestatic Singleton uniqueInstance =new Singleton();

/**

       * 注意这是一个私有构造器 ,也就是说单例模式只能自己实例化自己

        */

privateSingleton() {

//初始化

}

//提供了一个全局访问点

publicstatic Singleton instance() {

  returnuniqueInstance;

}

}

被动实例化(懒汉式):在第一次调用instance()方法时,对该类进行实例化。java源码:

public class Singleton {

      // 唯一私有静态引用

      private static Singleton uniqueInstance =null;

/**

       * 注意这是一个私有构造器 ,也就是说单例模式只能自己实例化自己

        */

      private Singleton() {

//初始化

}

//提供了一个全局访问点,在多线程中,需要加一个关键字:synchronized

  public static Singleton instance() {

         if(uniqueInstance == null)

uniqueInstance = new Singleton();

         return uniqueInstance;

      }

}


两种实例化方式的比较:

相比较而言,主动实例化(饿汉式)比较占用资源,但是由于加载时即初始化,性能会较被动实例化(饿汉式)要好一些。在多线程的环境下,主动实例化不会有线程安全问题,被动实例化(饿汉式),初始过程中,当多个线程同时访问时,则有可能获得多个实例。

多线程

上述被动实例化的源代码,多线程中可能会有多个实例的问题,在java语言中,我们可以加一个同步关键字synchronized解决,但是性能上,就会差很多。因为只有在实例化的时候才需要同步,在实例化之后,这个同步就成为了无用的且昂贵的开销。对此,有些人提出了在c++语言中常用的双重上锁成例来解决。

下面先解释一下c++中的双重上锁成例,源代码:

class Singleton:  

{  

public:  

//全局访问点,使用双重上锁成例(个人认为应该叫做双重判断)

static CSingleton * GetInstance()  

{

if (m_pInstance == NULL)  

{

Lock();//伪代码,我并不知道在c++中如何上锁

if(m_pInstance == NULL)

{

m_pInstance = new CSingleton();  

}

return m_pInstance;  

}

}

private:  

CSingleton(//初始化){};  //与java相同的私有构造函数

static CSingleton * m_pInstance; 

class CGarbo // 它的唯一工作就是在析构函数中删除CSingleton的实例  

{  

public:  

~CGarbo()  

{  

if (CSingleton::m_pInstance)  

delete CSingleton::m_pInstance;  

}  

};   

static CGarbo Garbo; // 定义一个静态成员,在程序结束时,系统会调用它的析构函数  

很遗憾,我并未在c++编程涉及过多线程,也未涉及到如何上锁,所以上述只能用伪代码写上锁了。下面解释一下为什么要双重上锁,和这个单例类的CGarbo内部类是做什么的。

双重上锁:

很显然,在多线程的情况下,可能有多个线程同时到达代码处,之后上锁。如果没有代码处的判断,那么多个线程依然会逐次的生成多个实例。反之,加上判断之后,第二个及以后的线程在代码处判断为假,直接返回已有的对象。

C++的内存回收:

C++中内存需要自己管理,那么这个私有静态变量m_pInstance指向的空间该如何释放?!

放到析构函数里是不成立的。

问:m_pInstance指针指向的对象什么时候会调用析构函数?

答:在我执行delete m_pInstance的时候。。。那我都显式调用了,自然会释放资源,要析构函数做什么?!(当然,如果该单例中还有其他资源需要释放的话,析构函数还是必要的),这种额外的显示调用容易被人遗忘,更可怕的是,如果有其他线程需要使用这个对象,让你给提前释放了。。。那就是个大的bug.

有人用了特别巧妙的方法,让让这个类自己知道在合适的时候把自己删除。C++中,程序在结束的时候,系统会自动析构所有的全局变量和类的静态成员变量。利用这个特征,我们可以在单例类中定义一个这样的静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例,即也就是上面我们看到的CGarbo内部类,(Garbo意为垃圾工人)。


遗憾的是,由于java语言规范与c++不同,java中,双重上锁成例不可实现,具体是由于什么java允许无序写入(我是没太弄懂),具体解释见如下网址:

中文:http://www.ibm.com/developerworks/cn/java/j-dcl.html

英文:http://www.ibm.com/developerworks/java/library/j-dcl/index.html


需要指出的是,主动实例化(饿汉式)在c++中比较难实现,因为静态初始化在c++中没有固定的顺序,因而静态的uniqueInstance变量的初始化与类的加载顺序没有保证,可能会出问题。所以在c++中经常使用被动实例化。而java,则常常使用主动实例化。

总结:

我们看到,如果不是在多线程中,单例模式可以说是异常的简单(多线程也不难),多线程的话,java建议使用主动实例化(饿汉式),被动(懒汉式)的话,要加同步关键字。C++中,多使用被动实例化。在c++中,很多人使用这种方法 http://blog.csdn.net/aaa20090987/article/details/7385081 来实现,好像是很好的方法。

什么时候使用单例模式?

单例模式不难,不代表它容易使用。关键是什么时候使用单例模式。以个人的理解,那些你觉得在工程中需要由“一个人”完成的工作,多使用单例模式。举一个网上常见的例子:一台计算机可以有多台打印机,而这个计算机上又可能有多个进程(或线程)想要使用打印机。这时你就希望有这么“一个人”,来统一的管理打印机,而不会出乱子,这就可以使用单例模式。同理,我们的程序中的配置类也通常是单例模式的,资源管理也通常是单例模式。网上说单例模式还可以作为通信媒介使用,也就是数据共享,它可以在不建立直接关联的条件下,让多个不相关的两个线程或者进程之间实现通信(我觉得这个比较牵强)。

当然,还有其他你觉得符合单例模式好处的场合。

单例模式的好处:

控制只有一个实例,节省资源;


单例模式要注意:

1.多线程时,除实例化外,要注意操作的线程安全。

2.据说不可以使用反射机制

3.据说也不可以序列化

相关文章推荐

学习笔记——JAVA设计模式<1>单例模式

Group of four GFO23种设计模式 创建型模式建对象 单例模式 工厂模式 抽象工厂模式 建造者模式原型模式 结构性模式 适配器模式 桥接模式 装饰模式 组合模式...

设计模式学习笔记——单例模式

定义: 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。 单例模式的通用类图如下: 代码如下: public class Singleton { private...

《从零开始学Swift》学习笔记(Day 63)——Cocoa Touch设计模式及应用之单例模式

原创文章,欢迎转载。转载请注明:关东升的博客  什么是设计模式。设计模式是在特定场景下对特定问题的解决方案,这些解决方案是经过反复论证和测试总结出来的。实际上,除了软件设计,设计模式也被广泛应用于其他...

JavaScript学习记录——《学用 JavaScript 设计模式》学习笔记(2)单例模式

单例模式 在JavaScript语言中, 单例服务作为一个从全局空间的代码实现中隔离出来共享的资源空间是为了提供一个单独的函数访问指针。 我们能像这样实现一个单例:...

设计模式C++学习笔记之六(Singleton单例模式)

单例模式看起来也蛮简单的,就是在系统中只允许产生这个类的一个实例,既然这么简单,就直接贴代码了。更详细的内容及说明可以参考原作者博客:cbf4life.cnblogs.com。 解释 ma...

设计模式学习笔记--单例模式

一.面向对象的设计原则 1.单一职责原则: 系统中每个对象只有一个单独的职责,对外提供一种功能,引起类变化的原因只有一个。 2.开闭原则:即对修改关闭,对扩展开放。要更改时是通过增加代码进行的,而...

java设计模式学习笔记1 单例模式

单例模式:指的是类 只能由自己生成唯一一个对象实例,并向其他对象提供这一实例。 实现单例模式有三种方法;常用的是所谓懒汉模式,与饿汉模式 1饿汉式:即在类在初始化的时候就已经自行实例化: publ...

java学习笔记-设计模式之单例模式

一,偶然想想到的如果把Java的构造方法弄成private,那里面的成员属性是不是只有通过static来访问呢;如果构造方法是private的话,那么有什么好处呢;如果构造方法是private的话,会...

Java设计模式学习笔记之单例模式

首先是单例模式的内容:         1、单例类只能有一个实例。   2、单例类必须自己创建自己的唯一实例。   3、单例类必须给所有其他对象提供这一实例 单例模式确保某个类只有一个实例,而...

设计模式学习笔记---单例模式(Java版)

GOF23(Group of  four) 创建型模式 单例模式,工厂模式,抽象工厂模式,建造者模式,原型模式。 结构型模式 适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:设计模式——单例模式(学习笔记)
举报原因:
原因补充:

(最多只允许输入30个字)