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

原创 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.据说也不可以序列化

设计模式系列(一)单例模式

一、简单介绍 单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。 《设计模式》书中对单例模式动机的介绍 对于系统中的某些类...
  • robertcpp
  • robertcpp
  • 2016年06月05日 11:00
  • 2229

设计模式(创建型)之单例模式(Singleton Pattern)

单例模式可能是23种设计模式中最简单的。应用也非常广泛,譬如Android中的数据库访问等操作都可以运用单例模式。核心概念: 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称...
  • yanbober
  • yanbober
  • 2015年04月27日 16:50
  • 2665

C++设计模式[二]单例模式

接着就是单例模式;个人理解就是把创建方法改为私有,然后再内部实例化,禁止外部创建对象。 何为单例模式,在GOF的《设计模式:可复用面向对象软件的基础》中是这样说的:保证一个类只有一个实例,并提供一个...
  • langb2014
  • langb2014
  • 2015年11月03日 16:11
  • 1076

设计模式:单例模式(Singleton)

单例模式在23个设计模式中算得上是最简单的一个了,也行你会有异议,那就换成“最简单之一”,这样就严谨了很多。   单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。   适用性:当类...
  • u013256816
  • u013256816
  • 2016年03月23日 21:37
  • 4924

使用Object-C实现23种设计模式之单例模式

今天给大家讲解一下第四种创建模式——单例模式,单例模式不同于前几种,前几种更注重逻辑,而单例模式则偏重于内存管理以及实际运用,相对前几种要相对简单。 所谓单例模式,就是一种让某个类只产生单一实例的模式...
  • u013054715
  • u013054715
  • 2016年05月23日 22:43
  • 4397

浅谈常见设计模式--单例模式 简单工厂模式

今题那站在这里和大家一起分享最近在一本书上看到的关于设计模式的内容,接下来要讲的设计模式有: 单例模式 简单工厂模式 工厂方法和抽象工厂 代理模式 命令模式 策略模式 门面模式 桥接模式 观察者模式 ...
  • xikai18827083487
  • xikai18827083487
  • 2016年11月13日 17:04
  • 969

设计模式(二)单例模式的七种写法

面试的时候,问到许多年轻的Android开发他所会的设计模式是什么,基本上都会提到单例模式,但是对单例模式也是一知半解,在Android开发中我们经常会运用单例模式,所以我们还是要更了解单例模式才对。...
  • itachi85
  • itachi85
  • 2016年01月17日 10:29
  • 24241

C#设计模式(1)——单例模式(讲解非常清楚)

一、引言 最近在学设计模式的一些内容,主要的参考书籍是《Head First 设计模式》,同时在学习过程中也查看了很多博客园中关于设计模式的一些文章的,在这里记录下我的一些学习笔记,一是为了...
  • u013472838
  • u013472838
  • 2015年04月02日 15:01
  • 706

C++设计模式5--单例模式Singleton--当前对象只有一个实例

很多情况下,我们在开发项目的过程中,都希望自己运行的某个部件只有一个实例, 比如我们天天用QT开发界面,QTCreate里帮助菜单下的关于Qt Create菜单,弹出来的关于对话框,在QTCreate...
  • gatieme
  • gatieme
  • 2014年01月08日 13:25
  • 21467

大话设计模式——单例模式

宏观导图 细节展示  单例模式的结构图: 关键代码:  在GetInstance方法中,要加入判断。 if (instance==null) { instance=new Single...
  • u011500356
  • u011500356
  • 2015年01月05日 21:44
  • 1464
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:设计模式——单例模式(学习笔记)
举报原因:
原因补充:

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