题目:设计一个类,我们只能生成该类的一个实例。

分析:只能生成一个实例的类是实现了Singleton模式的类型。

由于设计模式在面向对象程序设计中起着举足轻重的作用,在面试过程中很多公司都喜欢问一些与设计模式相关的问题。在常用的模式中,Singleton是唯一一个能够用短短几十行代码完整实现的模式。因此,写一个Singleton的类型是一个很常见的面试题。

事实上,要让一个类型是能创建一个实例不是一件很难的事情。我们可以把该类型的构造函数设为private,这样该类型的用户就不能创建该类型的实例了。然后我们在给类型中创建一个静态实例。当用户需要该类型的实例时,我们就返回这个实例。基于这个思路,我们可以用C#写出如下代码:

// We can only get an instance of the class Singleton1. The instance// is created when class Singleton1 is referenced at the first timepublic sealed class Singleton1{private Singleton1(){}private static Singleton1 instance = new Singleton1();public static Singleton1 Instance{get{return instance;}}}

由于类Singleton1 的实例是一个静态变量,因此它会在该类型的第一次引用的时候被创建,而不是第一次在调用Singleton1.get_Instance的时候被创建。如果我们此时并不需要该实例,那么我们就过早地初始化该实例,无论在内存空间还是CPU时间上都是一种浪费。

我们可以把上面的代码稍作改动,就能实现在第一次调用Singleton_getInstance时候才会创建类型的唯一实例:

 // We can only get an instance of the class Singleton2.// The instance is created when we need it explicitly.public sealed class Singleton2{private Singleton2(){}private static Singleton2 instance = null;public static Singleton2 Instance{get{if (instance == null)instance = new Singleton2();return instance;}}}

我们在单线程环境下只能得到类型Singleton2的一个实例,但在多线程环境下情况就可能不同了。设想如果两个线程同时运行到语句if (instance == null),而此时该实例的确没有创建,那么两个线程都会创建一个实例。此时,类型Singleton2就不在满足模式Singleton的要求了。

为了保证在多线程环境下我们还是只能得到类型的一个实例,我们应该在判断实例是否已经创建,以及在实例还没有创建的时候创建一个实例的语句上加一个同步锁。我们把Singleton2稍作修改就得到了如下代码:

 // We can only get an instance of the class Singleton3,// even when there are multiple threads which are trying// to get an instance concurrently.public sealed class Singleton3{private Singleton3(){}private static readonly object syncObj = new object();private static Singleton3 instance = null;public static Singleton3 Instance{get{lock (syncObj){if (instance == null)instance = new Singleton3();}return instance;}}}

说明一下,由于C/C++没有为线程同步提供直接的支持。为了让代码显得简洁,而不是让大量的代码在实现同步锁而偏离了实现Singleton的主题,本文的代码用C#实现。

我们还是假设有两个线程同时想创建一个实例。由于在一个时刻只能有一个线程能得到同步锁。当第一个线程加上锁时,第二个线程只能在等待。当第一个线程发现实例还没有创建时,它创建出一个实例。接着第一个线程释放同步锁。此时第二个线程可以加上同步锁,并运行接下来的代码。由于此时实例已经被第一个线程创建出来了,第二个线程就不会重复创建实例了。于是保证了我们只能得到一个实例。

但是类型Singleton3还不是完美。由于我们每次调用Singleton3.get_Instance的时候,都会试图加上一个同步锁。由于加锁是一个非常耗时的操作,在没有必要的时候我们应该尽量避免这样的操作。

实际上,我们只是在实例还没有创建之前需要加锁操作,以保证只有一个线程创建出实例。而当实例已经创建之后,我们已经不需要再做加锁操作了。于是,我们可以把上述代码再作进一步的改进:

  // We can only get an instance of the class Singleton4,// even when there are multiple threads which are trying// to get an instance concurrently. When the instance has// been created, we don't need the lock any more.public sealed class Singleton4{private Singleton4(){}private static object syncObj = new object();private static Singleton4 instance = null;public static Singleton4 Instance{get{if (instance == null){lock (syncObj){if (instance == null)instance = new Singleton4();}}return instance;}}}

我们只需要在最开始调用Singleton4_getInstance(可能来自一个线程,也可能来自多个线程)的时候需要加锁。当实例已经创建之后,我们就不再需要作加锁操作,从而在后续调用Singleton4_getInstance时性能得到提升。

关于第一种写法的更多,请参考http://en.wikipedia.org/wiki/Double-checked_locking。站在面试的角度,本文的分析已经足够应付。但如果想展示更多对多线程编程的理解,更深入地了解这个问题总是有益的。

本文已经收录到《剑指Offer——名企面试官精讲典型编程题》一书中,有改动,书中的分析讲解更加详细,增加了一种按需分配内存的方法。欢迎关注。

博主何海涛对本博客文章享有版权。网络转载请注明出处http://zhedahht.blog.163.com/。整理出版物请和作者联系。

程序员面试题精选100题(45)-Singleton(C/C++/C#)相关推荐

  1. 程序员面试题精选100题:41-50解题报告

    程序员面试题精选100题(41)-把数组排成最小的数[算法]   题目:输入一个正整数数组,将它们连接起来排成一个数,输出能排出的所有数字中最小的一个.例如输入数组{32,  321},则输出这两个能 ...

  2. 程序员面试题精选100题

    程序员面试题精选100题(01)-把二元查找树转变成排序的双向链表 题目:输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表.要求不能创建任何新的结点,只调整指针的指向. 比如将二元查找树   ...

  3. [程序员面试题精选100题]13.第一个只出现一次的字符

    [题目] 在一个字符串中找到第一个只出现一次的字符.如输入abaccdeff,则输出b. [分析] [代码] /********************************* * 日期:2013- ...

  4. 程序员面试题精选100题(51)-顺时针打印矩阵

    // 程序员面试题精选100题(51)-顺时针打印矩阵.cpp : 定义控制台应用程序的入口点. //#include "stdafx.h" #include <iostre ...

  5. 程序员面试题精选100题:求从1到n的正数中1出现的次数

    // 程序员面试题精选100题(25):求从1到n的正数中1出现的次数 // 如 f(253) = (2!=0) * 100 + 2 * f(99) + (5!=0) * 10 + 5 * f(9) ...

  6. 程序员面试题精选100题:11-40解题报告

    程序员面试题精选100题(11)-求二元查找树的镜像[数据结构]   题目:输入一颗二元查找树,将该树转换为它的镜像,即在转换后的二元查找树中,左子树的结点都大于右子树的结点.用递归和循环两种方法完成 ...

  7. [程序员面试题精选100题]19.反转链表

    题目 输入一个链表的头结点,反转该链表,并返回反转后链表的头结点. 分析 假设经过若干操作,我们已经把结点 pre之前的指针调整完毕,这些结点的next指针都指向前面一个结点.现在我们遍历到结点cur ...

  8. 程序员面试题精选100题(03)-子数组的最大和[算法]

    题目:输入一个整形数组,数组里有正数也有负数.数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和.求所有子数组的和的最大值.要求时间复杂度为O(n). 例如输入的数组为1, -2, 3, ...

  9. python程序员面试题精选100题_在Python程序员面试中被问的最多的10道题

    我们在为大家整Python程序员面试试题中,发现了一些被面试官问到的最多的一些问题,以下就是本篇内容: Python是个非常受欢迎的编程语言,随着近些年机器学习.云计算等技术的发展,Python的职位 ...

最新文章

  1. hdu 1753大小数相加
  2. python远程ftp服务器文件,如何将远程python FTP服务器连接到本地pythonftp客户端
  3. 道路运输车辆卫星定位系统JT/T808服务实现和压测
  4. 中国深圳,600架无人机的盛典!
  5. LeetCode 257 二叉树的所有路径
  6. 南林计算机课程表,南京林业大学课程表.doc
  7. Tomcat配置可以访问外部资源路径
  8. (转)ATOM介绍和使用
  9. python基础教程自学网-Python基础系统管理学习手册视频教程
  10. angularjs compile和link
  11. 机器翻译市场需求调研报告
  12. DLL注入之全局钩子注入
  13. ArduinoUNO测试VEML6075紫外线传感器
  14. 新能源与材料如何应用计算机,计算机模拟在光电材料及太阳能工程领域的应用与新进展.doc...
  15. 二十余岁时的社会观(一)
  16. HTML文档繁转简,excel简繁转换 切换到“审阅”,点击“繁转简”:
  17. 如何删除搜索框的搜索记录(谷歌浏览器)
  18. 一句话+一张图——说清楚Aprioir关联规则算法
  19. CTFshow击剑杯osint——人家想玩嘛,人有点多超详细解法
  20. 独孤思维:没有复盘的项目,不会赚钱

热门文章

  1. 神经网络有什么理论支持? 本文作者:AI研习社 2017-11-08 18:30 导语:问:神经网络有什么理论支持? 答:目前为止(2017 年)没有什么特别靠谱的。 雷锋网按:本文原作者袁洋
  2. mysql开启定时器_MySQL定时器开启、调用实现代码
  3. MySQL-分库分表初探
  4. VirtualBox安装Kali
  5. 【栈和队列】数据结构02-(java实现)
  6. html5 滤色,深入理解CSS mix-blend-mode滤色screen混合模式
  7. HashMap、Hashtable、ConcurrentHashMap的原理与区别
  8. hashmap 判断key是否存在
  9. pytest第一版 入门学习
  10. 获取打印的html代码怎么写,javascript如何输出?