热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

单例模式虽好但请不要滥用

说到SingleTon,我相信只要知道设计模式的,就知道SingleTon,也写过SingleTon,可谓是尽人皆知的设计模式了。就是这个尽人皆知的设计模式,却是被滥用的最厉害的设计模式,本篇就讨论一下关于SingleTon的滥用问题。首先GoF是站在一个纯OO的领域思考问题的,所以,很多其他领域的问题并没有考虑进来,

说到面向对象的设计模式,现在很多人都可以随便说出好几种常用的,但是有没有想过设计模式,即使是初学者也至少能说一下SingleTon和Factory Method这两个。那么,设计模式是不是随便怎么用都没问题哪?

这个问题从提问的方式上就可以看出,答案一定是否定的(大家也不是白白接受了这么多年的应试教育的)。 但是,就我个人的观察,滥用设计模式的绝对不是少数。而且越是简单的模式越会被滥用。

从最简单的模式——SingleTon开始。

说到SingleTon,我相信只要知道设计模式的,就知道SingleTon,也写过SingleTon,可谓是尽人皆知的设计模式了。就是这个尽人皆知的设计模式,却是被滥用的最厉害的设计模式,本篇就讨论一下关于SingleTon的滥用问题。

线程安全是个问题

首先GoF是站在一个纯OO的领域思考问题的,所以,很多其他领域的问题并没有考虑进来(事实上也不适合拉进来一起讲),然而实际编程者却不得不面对更多领域的问题(最常见的是并发领域)。这也就是为什么在GoF的SingleTon是如此的简单,而在Java或.net实际写SingleTon时,却需要注意锁的问题的根本原因。关于SingleTon是否是线程安全的,我倾向于把问题分解成两个部分:

  1. SingleTon本身是否线程安全
  2. SingleTon的实例是否线程安全

关于第一个,通过著名的Java下双检锁不安全问题,相信大家都已经十分清楚了,如果还有不清楚的,请查阅相关资料,本文就不再重复了。在这里,要重点说的是第二个问题,SingleTon的实例是否线程安全。

如果仔细看msdn的话,在大多数类上,msdn都写了这么一句话:Public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

在msdn中文版中是:此类型的公共静态(在 Visual Basic 中为 Shared)成员是线程安全的。但不能保证任何实例成员是线程安全的。

在ms给出的类库,把静态成员都写成线程安全的,而对待大部分的实例成员却是放任其线程的不安全,而要求开发人员在开发时处理实例成员的线程不安全问题(我相信Java的类库也是类似的做法)。

那么在多线程程序中使用SingleTon,会引出什么问题哪?首先,SingleTon仅仅允许一个类只有一个实例。那么这里简单的做个推理:

  1. 在这个程序里面无论何时,当你需要这个类的实例的时候,都只能使用这个唯一的实例。
  2. 无论在这个程序的哪个线程中,当你需要这个类的实例的时候,都只能使用这个唯一的实例。
  3. 无论在这个程序中出现什么样的并发,当你需要这个类的实例的时候,都只能使用这个唯一的实例。
  4. 无论在这个程序中出现什么样的并发,当你需要调用这个类的实例的某个成员时,都只能使用这个唯一的实例的成员。
  5. 无论在这个程序中出现什么样的并发,为了保证程序是线程安全的,当你需要调用这个类的实例的某个成员时,都只能使用这个唯一的实例的成员,并且需要保证其线程安全。
  6. 无论在这个程序中出现什么样的并发,为了保证程序是线程安全的,当你需要调用这个类的实例的某个成员时,如果这个成员不是线程安全的,那么在使用这个唯一的实例的成员时都需要正确的线程同步。

发现问题了没有,被SingleTon的实例成员的线程安全性需要谁来保证?

  • 选项A:SingleTon的实例保证所有的成员是线程安全的
  • 选项B:SingleTon的实例不保证线程安全的,请大家在使用的时候都加上锁

如果选择A,那么,请检查那些被SingleTon的代码,看看有没有用到堆,检查对堆的任何调用是否都是线程安全的,或者已经同步的。

如果选择B,那么,请检查所有使用SingleTon的代码,看看是否都经过了可靠的线程同步(例如Lock那个SingleTon的对象),只要有一处不注意,就导致整个是线程不安全的(但是当下这么多写网页的人,有多少人会去关注那些资源是需要线程同步的吗?)。

有没有选项C?有,如果是类库的话,对外宣称程序是线程不安全的,请在使用前保证线程安全(例如COM中著名的STA);如果是应用程序的话就告诉客户,本程序是线程不安全的,出任何问题都是有可能的,当初合同就没说要保证线程安全(客户一定会抓狂)。

为什么要用SingleTon?

不知道大家有没有想过,当初为什么要把这个类型用SingleTon来做。我看到的大多数答案是节省资源和全局状态,固定算法的接口适配(不排除还有更好的答案)。

先讨论节省资源的问题,首先不new实例一定比new实例要节省资源(CPU和内存),这点不用质疑,但是如果要做到线程安全,似乎就要再考量一下了。

如果SingleTon实例本身的实现方式就保证了线程安全(仅使用堆栈和参数中的对象,对自身引用的对象只读),那么线程安全是0代价的。如果其实现方式涉及Lock等同步,那么冲突概率是多少,如果冲突概率足够的高,那么大多数时间线程将进入等待状态,导致大量占用时间资源和CPU资源。即使冲突概率很低,由于Lock需要同步Cache和内存,所以一样需要花费一些额外的代价(同步Cache的时间代价)。

说到这里,还觉得有Lock的SingleTon一定比new实例节省资源?未必吧,到底谁省资源还是要具体问题具体分析一下,不Profiling一下,谁又知道结果哪?

至于全局状态,可以的话,要尽量避免全局状态的使用,如果必须要使用的话,确实没什么好的方案,不过,建议作为全局状态使用的SingleTon需要保证所有成员的线程安全(否则,一个team member的小错误,就可能导致全局状态出错)。

第三种固定算法的接口适配,这是我比较提倡的SingleTon用法。这里涉及几个部分的要求:

  1. 算法固定
  2. 算法不存在状态,所有的变化均来自参数,且实现无需线程同步就可以保证线程安全
  3. 接收方要求符合某接口(泛指)

如果第一点不满足,那就不可能作为SingleTon存在。第二点是确保使用SingleTon的性能优势。而第三点是OO立场上的SingleTon必要性,否则为什么不用静态方法。

也许这3点比较抽象,举个实际点的例子:

public class PersonNameComparer
    : IComparer
{
    public int Compare(Person x, Person y)
    {
        if (x == y)
            return 0;
        if (x == null)
            return -1;
        if (y == null)
            return 1;
        return string.Compare(x.name, y.name);
    }
}

第一,算法固定,就是比较2个人的名字,没有第二种算法,要是有那也是其它类的职责;第二,不存在状态,只用了x和y两个参数,没有线程同步;第三,因为需要在排序的场合需要IComparer接口的实例,因此不能使用静态方法(假设不能使用委托)。

因此,这个类型如果是SingleTon的话,那将是合理的(当然这里不用SingleTon也没问题)。

后话

N年前,我去面试某公司时,某面试官问我数据库联接能不能做SingleTon,我说不能,会引入很多问题,结果面试官很不满意。

N月前,面试某人,在谈到设计模式用在什么地方时候,举例说在Web项目中把一个WebService的代理类做成了SingleTon,结果我很不满意。哎,SingleTon啊SingleTon,真是GoF引入OO的最大的坑。

本文地址:http://www.nowamagic.net/librarys/veda/detail/921,欢迎访问原出处。


推荐阅读
  • 生成对抗式网络GAN及其衍生CGAN、DCGAN、WGAN、LSGAN、BEGAN介绍
    一、GAN原理介绍学习GAN的第一篇论文当然由是IanGoodfellow于2014年发表的GenerativeAdversarialNetworks(论文下载链接arxiv:[h ... [详细]
  • 学习SLAM的女生,很酷
    本文介绍了学习SLAM的女生的故事,她们选择SLAM作为研究方向,面临各种学习挑战,但坚持不懈,最终获得成功。文章鼓励未来想走科研道路的女生勇敢追求自己的梦想,同时提到了一位正在英国攻读硕士学位的女生与SLAM结缘的经历。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • “你永远都不知道明天和‘公司的意外’哪个先来。”疫情期间,这是我们最战战兢兢的心情。但是显然,有些人体会不了。这份行业数据,让笔者“柠檬” ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 无线认证设置故障排除方法及注意事项
    本文介绍了解决无线认证设置故障的方法和注意事项,包括检查无线路由器工作状态、关闭手机休眠状态下的网络设置、重启路由器、更改认证类型、恢复出厂设置和手机网络设置等。通过这些方法,可以解决无线认证设置可能出现的问题,确保无线网络正常连接和上网。同时,还提供了一些注意事项,以便用户在进行无线认证设置时能够正确操作。 ... [详细]
  • 本文详细介绍了相机防抖的设置方法和使用技巧,包括索尼防抖设置、VR和Stabilizer档位的选择、机身菜单设置等。同时解释了相机防抖的原理,包括电子防抖和光学防抖的区别,以及它们对画质细节的影响。此外,还提到了一些运动相机的防抖方法,如大疆的Osmo Action的Rock Steady技术。通过本文,你将更好地理解相机防抖的重要性和使用技巧,提高拍摄体验。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文介绍了Python版Protobuf的安装和使用方法,包括版本选择、编译配置、示例代码等内容。通过学习本教程,您将了解如何在Python中使用Protobuf进行数据序列化和反序列化操作,以及相关的注意事项和技巧。 ... [详细]
  • MACElasticsearch安装步骤及验证方法
    本文介绍了MACElasticsearch的安装步骤,包括下载ZIP文件、解压到安装目录、启动服务,并提供了验证启动是否成功的方法。同时,还介绍了安装elasticsearch-head插件的方法,以便于进行查询操作。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
author-avatar
朱鹏飞0521
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有