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

设计模式之单例模式(二)

设计模式之单例模式(二),Go语言社区,Golang程序员人脉社

上一篇我们对经典的单例模式进行了学习,并且知道了单例模式的概念,以及如何通过单线程去创建一个有效的单例模式,让程序不用多次去创建实例。

但是,通过巧克力工厂的实践,我们很想知道在多线程模式下,这个到底会是什么情况呢?所以,就有了我们继续学习的目标啦。原来单例模式,不简单呀。

多线程的麻烦

首先,我们还是看下巧克力工厂经典单例的代码:原本在单线程模式下,运行的还是挺好的,工厂里那些小心翼翼的代码都可以去掉了;但是忽然用了多线程,发现还是创建了多个实例,当工厂很郁闷。

public static ChocolateBoiler getInstance() {
    if (uniqueInstance == null) {
        uniqueInstance = new ChocolateBoiler(;)
    }
    return uniqueInstance;
}

仔细查看代码,你知道为什么了吗?这里有两个线程都要执行这段代码,那么Java的JVM在进入代码的时候肯定会有先后顺序,有没有可能是JVM搅乱了代码,让getInstance()方法内部出了问题呢?对的,就是这样。请看下图:

当在多线程环境下,线程1和线程2都进入了getInstance方法,那么,此时通过判断null的方式来,势必在JVM内部,发生了交叉的事情,然后,然后你的工厂就创建了两个实例,挺悲剧的。

处理多线程

熟悉Java的朋友都知道,最轻易地解决这个多线程的方法就是把getInstantce()变成synchronized方法,比如:

public static synchronized Singleton getInstance() {
    if(uniqueInstance == null) {
        uniqueInstance = new Singleton();
    }
    return uniqueInstance;
}

通过增加synchronized关键字到getInstance()方法中,每个线程在进入这个方法之前,需要先等候别的线程离开该方法,也就是说,不会有两个线程可以同时进入这个方法。

但是,问题来了:我们其实只有第一次执行此方法时,才真正需要同步。换句话说,一旦设置好uniqueInstance变量,就不需要同步这个方法了。之后每次调用这个方法,如果还是同步进行的话,给资源造成了很大的浪费,也是一种累赘。

能改善多线程吗?

为了符合大多数Java应用程序、我们还是需要确保单例模式能在多线程的情况下正常工作的。但是同步的getInstance()的做法将拖垮性能,该怎么办呢?

  1. 如果getInstance()的性能对应用程序不是很关键,就什么都别做

没错,如果你的应用程序可以接受getInstance()造成的额外负担,就忽略了吧。同步getInstance()的方法既简单又有效。但是你必须知道,同步一个方法可能造成程序执行效率下降100倍。所以,还是得好好考虑应用场景哦。

  1. 使用“急切”创建实例,而不用延迟实例化的做法

如果应用程序总是创建并使用单例实例,或者在创建和运行时方面的负担不太繁重,你可能想要急切(eagerly)创建此单例,比如:

public class Singleton {
    // 在静态初始化中创建单例,这段代码保证了线程安全
    private static Singleton uniqueInstance = new Singleton();
    
    private Singltton() {}
    
    public static Singleton getInstance() {
        return uniqueInstance;
    }
}

利用这个做法,我们依赖JVM在加载这个类时马上创建此唯一的单例实例。JVM保证在任何线程访问uniqueInstance静态变量之前,一定先创建。哈哈,有没有,这就是饿汉式。

  1. 用双重检查加锁,在getInstance()中减少使用同步

利用双重检查加锁(double-checked locking),首先检查是否实例已经创建了,如果尚未创建,才进行同步。这样 依赖,只有第一次会同步,这正是我们想要的。

public class Singleton {
    private volatile static Singleton uniqueInstance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
    // 检查实例,如果不存在,就进入同步区块,只有第一次才彻底执行这里的代码
        if (uniqueInstance == null) {
            synchronized (Singleton.class) {
    // 进去区块后,再检查一次,如果仍是null,才创建实例
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

volatile关键词确保:当uniqueInstance变量被初始化成Singleton实例时,多个线程正确地处理uniqueInstance变量。

因为单例模式大家用的比较多,而且思路也比较清晰了,那我就在这里把这章总结也一并附上了。

设计箱内的工具

还是按照之前的套路,总结下工具箱内新增的工具吧

  • OO基础

    抽象、封装、继承、多态

  • OO原则

    封装变化

    多用组合,少用继承

    针对接口编程,不针对实现编程

    为交互对象之间的松耦合设计而努力

    依赖抽象,不要依赖具体类

    类应该对扩展开放,对修改关闭

  • OO模式

    『策略模式』、『观察者模式』、『装饰者模式』、『抽象工厂模式』、『工厂方法模式』

    单例模式』确保一个类只有一个实例,并提供全局访问点

好的,很开心有没有,又学会了一个设计模式,还是我们经常使用的设计模式之一。下一次,我们将学习命令模式,和大家不见不散。

爱生活,爱学习,爱感悟,爱挨踢


推荐阅读
  • Java和JavaScript是什么关系?java跟javaScript都是编程语言,只是java跟javaScript没有什么太大关系,一个是脚本语言(前端语言),一个是面向对象 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 恶意软件分析的最佳编程语言及其应用
    本文介绍了学习恶意软件分析和逆向工程领域时最适合的编程语言,并重点讨论了Python的优点。Python是一种解释型、多用途的语言,具有可读性高、可快速开发、易于学习的特点。作者分享了在本地恶意软件分析中使用Python的经验,包括快速复制恶意软件组件以更好地理解其工作。此外,作者还提到了Python的跨平台优势,使得在不同操作系统上运行代码变得更加方便。 ... [详细]
  • 2018年人工智能大数据的爆发,学Java还是Python?
    本文介绍了2018年人工智能大数据的爆发以及学习Java和Python的相关知识。在人工智能和大数据时代,Java和Python这两门编程语言都很优秀且火爆。选择学习哪门语言要根据个人兴趣爱好来决定。Python是一门拥有简洁语法的高级编程语言,容易上手。其特色之一是强制使用空白符作为语句缩进,使得新手可以快速上手。目前,Python在人工智能领域有着广泛的应用。如果对Java、Python或大数据感兴趣,欢迎加入qq群458345782。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
  • 本文介绍了RxJava在Android开发中的广泛应用以及其在事件总线(Event Bus)实现中的使用方法。RxJava是一种基于观察者模式的异步java库,可以提高开发效率、降低维护成本。通过RxJava,开发者可以实现事件的异步处理和链式操作。对于已经具备RxJava基础的开发者来说,本文将详细介绍如何利用RxJava实现事件总线,并提供了使用建议。 ... [详细]
  • SpringBoot整合SpringSecurity+JWT实现单点登录
    SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 从零学Java(10)之方法详解,喷打野你真的没我6!
    本文介绍了从零学Java系列中的第10篇文章,详解了Java中的方法。同时讨论了打野过程中喷打野的影响,以及金色打野刀对经济的增加和线上队友经济的影响。指出喷打野会导致线上经济的消减和影响队伍的团结。 ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了软件测试知识点之数据库压力测试方法小结相关的知识,希望对你有一定的参考价值。 ... [详细]
author-avatar
干将stambach_611
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有