一、单例模式的介绍

  说到单例模式,大家第一反应应该就是--什么是单例模式?从“单例”字面意思上理解:一个类只有一个实例。所以单例模式也就是保证一个类只有一个实例的一种实现方法罢了(设计模式其实就是帮助我们解决实际开发过程中的方法,该方法是为了降低对象之间的耦合度。然而,解决方法有很多种,所以前人就总结了一些常用的解决方法,就称为设计模式),下面给出单例模式的一个官方定义:确保一个类只有一个实例,并提供一个全局访问点。为了帮助大家更好地理解单例模式,大家可以结合下面的类图来进行理解,后面也会剖析单例模式的实现思路:

  二、为什么会有单例模式

  看完单例模式的介绍,自然大家都会有这样一个疑问--为什么要有单例模式?它在什么情况下使用?从单例模式的定义中我们可以看出--单例模式的使用自然是当我们的系统中某个对象只需要一个实例的情况,例如:操作系统中只能有一个任务管理器;操作文件时,同一时间内只允许一个实例对其操作等,既然现实生活中有这样的应用场景,自然在软件设计领域必须有这样的解决方案了(因为软件设计也是现实生活中的抽象),所以也就有了单例模式了。

  三、剖析单例模式的实现思路

  了解完了一些关于单例模式的基本概念之后,下面就为大家剖析单例模式的实现思路的,因为在我自己学习单例模式的时候,咋一看单例模式的实现代码确实很简单,也很容易看懂。但是我还是觉得它很陌生,而且心里总会这样一个疑问--为什么前人会这样去实现单例模式呢?他们是如何思考的?

  我们从单例模式的概念(确保一个类只有一个实例,并提供一个访问它的全局访问点)入手,可以把概念进行拆分为两部分:(1)确保一个类只有一个实例;(2)提供一个访问它的全局访问点;下面通过采用两人对话的方式来帮助大家更快掌握分析思路:
  菜鸟:怎样确保一个类只有一个实例了?
  老鸟:那就让我帮你分析下,你创建类的实例会想到用什么方式来创建的呢?

  菜鸟:用new关键字啊,只要new下就创建了该类的一个实例了,之后就可以使用该类的一些属性和实例方法了。
  老鸟:那你想过为什么可以使用new关键字来创建类的实例吗?

  菜鸟:这个还有条件的吗?........., 哦,我想起来了,如果类定义私有的构造函数就不能在外界通过new创建实例了(注:有些初学者就会问,有时候我并没有在类中定义构造函数为什么也可以使用new来创建对象,那是因为编译器在背后做了手脚了,当编译器看到我们类中没有定义构造函数,此时编译器会帮我们生成一个公有的无参构造函数)
  老鸟:不错,回答的很对,这样你的疑惑就得到解答了啊!

  菜鸟:那我要在哪里创建类的实例?
  老鸟:你傻啊,当然是在类里面创建了(注:这样定义私有构造函数就是上面的一个思考过程:要创建实例,自然就要有一个变量来保存该实例,所以就有了私有变量的声明。但是实现中是定义静态私有变量,朋友们有没有想过--这里为什么定义为静态的呢?对于这个疑问的解释为:每个线程都有自己的线程栈,定义为静态主要是为了在多线程确保类有一个实例)

  菜鸟:哦,现在完全明白了,但是我还有另一个疑问——现在类实例创建在类内部,那外界如何获得该的一个实例来使用它了?
  老鸟:这个,你可以定义一个公有方法或者属性来把该类的实例公开出去了(注:这样就有了公有方法的定义了,该方法就是提供访问类的全局访问点)

  通过上面的分析,相信大家也就很容易写出单例模式的实现代码了,下面就看看具体的实现代码,看完之后你会惊讶道:真是这样的!

    /// <summary>
/// 单例模式的实现
/// </summary>
public class Singleton
{
// 定义一个静态变量来保存类的实例
private static Singleton uniqueInstance; // 定义私有构造函数,使外界不能创建该类实例
private Singleton()
{
} /// <summary>
/// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
// 如果类的实例不存在则创建,否则直接返回
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}

  上面的单例模式的实现在单线程下确实是完美的,然而在多线程的情况下会得到多个Singleton实例,因为在两个线程同时运行GetInstance方法时,此时两个线程判断(uniqueInstance ==null)这个条件时都返回真。此时两个线程就都会创建Singleton的实例,这样就违背了我们单例模式初衷了。既然上面的实现会运行多个线程执行,那我们对于多线程的解决方案自然就是使GetInstance方法在同一时间只运行一个线程运行就好了,也就是我们线程同步的问题了(对于线程同步大家也可以参考线程同步的文章),具体的解决多线程的代码如下:

  /// <summary>
/// 单例模式的实现
/// </summary>
public class Singleton
{
// 定义一个静态变量来保存类的实例
private static Singleton uniqueInstance; // 定义一个标识确保线程同步
private static readonly object locker = new object(); // 定义私有构造函数,使外界不能创建该类实例
private Singleton()
{
} /// <summary>
/// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
// 当第一个线程运行到这里时,此时会对locker对象 "加锁",
// 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
// lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
lock (locker)
{
// 如果类的实例不存在则创建,否则直接返回
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
} return uniqueInstance;
}
}

  上面这种解决方案确实可以解决多线程的问题,但是上面代码对于每个线程都会对线程辅助对象locker加锁之后再判断实例是否存在。对于这个操作完全没有必要的,因为当第一个线程创建了该类的实例之后,后面的线程此时只需要直接判断(uniqueInstance==null)为假。此时完全没必要对线程辅助对象加锁之后再去判断,所以上面的实现方式增加了额外的开销,损失了性能,为了改进上面实现方式的缺陷,我们只需要在lock语句前面加一句(uniqueInstance==null)的判断就可以避免锁所增加的额外开销,这种实现方式我们就叫它 “双重锁定”,下面具体看看实现代码的:

  /// <summary>
/// 单例模式的实现
/// </summary>
public class Singleton
{
// 定义一个静态变量来保存类的实例
private static Singleton uniqueInstance; // 定义一个标识确保线程同步
private static readonly object locker = new object(); // 定义私有构造函数,使外界不能创建该类实例
private Singleton()
{
} /// <summary>
/// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
// 当第一个线程运行到这里时,此时会对locker对象 "加锁",
// 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
// lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
// 双重锁定只需要一句判断就可以了
if (uniqueInstance == null)
{
lock (locker)
{
// 如果类的实例不存在则创建,否则直接返回
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}

  四、C#中实现了单例模式的类

  理解完了单例模式之后,菜鸟又接着问了:.NET FrameWork类库中有没有单例模式的实现呢?
  经过查看,.NET类库中确实存在单例模式的实现类,不过该类不是公开的,下面就具体看看该类的一个实现,该类具体存在于System.dll程序集,命名空间为System,大家可以用反射工具Reflector去查看源码:

// 该类不是一个公开类
// 但是该类的实现应用了单例模式
internal sealed class SR
{
private static SR loader;
internal SR()
{
}
// 主要是因为该类不是公有,所以这个全部访问点也定义为私有的了
// 但是思想还是用到了单例模式的思想的
private static SR GetLoader()
{
if (loader == null)
{
SR sr = new SR();
Interlocked.CompareExchange<SR>(ref loader, sr, null);
}
return loader;
} // 这个公有方法中调用了GetLoader方法的
public static object GetObject(string name)
{
SR loader = GetLoader();
if (loader == null)
{
return null;
}
return loader.resources.GetObject(name, Culture);
}
}

  五、总结

  到这里,设计模式的单例模式就介绍完了,希望通过本文章大家可以对单例模式有一个更深的理解,并且希望之前没接触过单例模式或觉得单例模式陌生的朋友看完之后会惊叹:原来如此!

参考链接:http://www.cnblogs.com/zhili/p/SingletonPatterm.html

【1】Singleton模式(单例模式)的更多相关文章

  1. Singleton模式(单例模式) 饿汉式和懒汉式

    目的:整个应用中有且只有一个实例,所有指向该类型实例的引用都指向这个实例. 好比一个国家就只有一个皇帝(XXX),此时每个人叫的“皇帝”都是指叫的XXX本人; 常见单例模式类型: 饿汉式单例:直接将对 ...

  2. 单例模式 | C++ | Singleton模式

    Singleton 模式 单例模式(Singleton Pattern)是 C++/Java等语言中最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 这种模式实 ...

  3. 设计模式之Singleton模式和Strategy模式是什么

    Singleton模式 单例模式,也交单子模式,有时候系统只需要拥有一个全局对象. 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建.这个类提供了一种访问其唯一的对象的方 ...

  4. 6、单例模式 Singleton模式 只有一个实例 创建型模式

    1.了解Singleton模式 程序在运行时,通常都会生成很多实例.例如,表示字符串的java . lang . string类的实例与字符串是- -对- -的关系,所以当有1000个字符串的时候,会 ...

  5. 单例模式/singleton模式/创建型模式

    Java实现要点: 私有构造方法 线程安全(并发的考虑) 延迟加载(效率的考虑,对于较大的类在使用时在加载) 公有方法访问单一实例 常见单例模式代码及问题 //无延迟加载,常驻内存(即使不使用) cl ...

  6. 剑指Offer面试题:1.实现Singleton模式

    说来惭愧,自己在毕业之前就该好好看看<剑指Offer>这本书的,但是各种原因就是没看,也因此错过了很多机会,后悔莫及.但是后悔是没用的,现在趁还有余力,把这本书好好看一遍,并通过C#通通实 ...

  7. Singleton Pattern单例模式

    单例模式是一种对象创建模式,它用于产生一个对象的具体实例,它可以确保系统中一个类只产生一个实例.Java里面实现的单例是一个虚拟机的范围,因为装载类的功能是虚拟机的,所以一个虚拟机在通过自己的 Cla ...

  8. Singleton模式——对象创建型模式

    Singleton模式即为单例模式/单件模式. (一)意图--保证一个类仅有一个实例,并提供一个访问它的全局访问点. 如一台计算机可以有多个端口,但是应该统一管理这些端口,避免访问冲突.--选择Sin ...

  9. 转:Singleton模式

    C++完美实现Singleton模式  转自:http://www.cppblog.com/dyj057/archive/2005/09/20/346.html boost库的Singleton的实现 ...

  10. Objective-C的singleton模式

    最近因为在ios应用开发中,考虑到一些公共方法的封装使用,就决定使用单例模式的写法了..不知道,Object-c中的单例模式的写法是否和java中的写法是否有所区别?于是阿堂从网上一搜,发现“ Obj ...

随机推荐

  1. 【2019年OCP新题】OCP题库更新出现大量新题-10

    10.Which two statements are true about SQL*Loader Express Mode in an Oracle 12c database? A) It can ...

  2. python format()函数的用法

    Python format() 函数的用法 复制自博主 chunlaipiupiupiu 的博客,如有侵权,请联系删除 python中format函数用于字符串的格式化 通过关键字 1 print(' ...

  3. Java并发工具类之并发数控制神器Semaphore

    Semaphore(信号量)使用来控制通知访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源. 我们可以这么理解Semaphore,比如一个厕所只有6个坑,同时只能满足6个人上厕所( ...

  4. Storm的并行度

    在Storm集群中,运行Topolopy的实体有三个:工作进程,executor(线程),task(任务),下图可以形象的说明他们之间的关系. 工作进程 Storm集群中的一台机器会为一个或则多个To ...

  5. POJ 1062

    #include<iostream> #include<stdio.h> #define MAXN 105 #define inf 10000000 using namespa ...

  6. AngularJS常用Directives实例

    在第一篇 认识AngularJS 中,我们已经基本了解了AngularJS,对Directive也有了一定了解,本章我们将继续介绍Directive,对其有一个更深入的了解和掌握. 常用的Direct ...

  7. SQL命令导入导出

    利用cmd的操作命令导出,详情如下(备注:方法二是转载网上的教程):1:G:\Oracle\product\10.1.0\Client_1\NETWORK\ADMIN目录下有个tnsname.ora文 ...

  8. vs2013 with update2安装记录

    1.安装之后,提示update2无法安装2.按照以下方法进行修复,提示无法安装的项更多了(1项变3项了) http://jingyan.baidu.com/article/6d704a13f34d10 ...

  9. Mysql 断电数据损毁恢复

    error log: Database page corruption on disk or a failed 处理: /etc/my.cnf 设置 innodb_force_recovery = 6 ...

  10. IntelliJ IDEA使用心得之问题篇;

    在使用IDEA的时候,难免会遇到一些问题,总结下来,一是备忘,而是分享. 1.IDEA代码编辑区无法编辑? 当系统时间被调整到当前时间以前时会导致IDEA无法编辑,有3中解决方案:1)重启IDEA:( ...