热门标签 | 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内存模型(JMM)的happens-before规则及其对多线程编程的影响。文章详细介绍了DCL双重检查锁定模式的问题及解决方案,并总结了不同处理器和内存模型之间的关系,旨在为程序员提供更深入的理解和最佳实践。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • 深入解析 Apache Shiro 安全框架架构
    本文详细介绍了 Apache Shiro,一个强大且灵活的开源安全框架。Shiro 专注于简化身份验证、授权、会话管理和加密等复杂的安全操作,使开发者能够更轻松地保护应用程序。其核心目标是提供易于使用和理解的API,同时确保高度的安全性和灵活性。 ... [详细]
  • FinOps 与 Serverless 的结合:破解云成本难题
    本文探讨了如何通过 FinOps 实践优化 Serverless 应用的成本管理,提出了首个 Serverless 函数总成本估计模型,并分享了多种有效的成本优化策略。 ... [详细]
  • 深入解析Java虚拟机(JVM)架构与原理
    本文旨在为读者提供对Java虚拟机(JVM)的全面理解,涵盖其主要组成部分、工作原理及其在不同平台上的实现。通过详细探讨JVM的结构和内部机制,帮助开发者更好地掌握Java编程的核心技术。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 本文探讨了在Java多线程环境下,如何确保具有相同key值的线程能够互斥执行并按顺序输出结果。通过优化代码结构和使用线程安全的数据结构,我们解决了线程同步问题,并实现了预期的并发行为。 ... [详细]
  • 本文总结了Java程序设计第一周的学习内容,涵盖语言基础、编译解释过程及基本数据类型等核心知识点。 ... [详细]
  • 作者:守望者1028链接:https:www.nowcoder.comdiscuss55353来源:牛客网面试高频题:校招过程中参考过牛客诸位大佬的面经,但是具体哪一块是参考谁的我 ... [详细]
  • 本文探讨了如何在日常工作中通过优化效率和深入研究核心技术,将技术和知识转化为实际收益。文章结合个人经验,分享了提高工作效率、掌握高价值技能以及选择合适工作环境的方法,帮助读者更好地实现技术变现。 ... [详细]
  • 本文作者分享了在阿里巴巴获得实习offer的经历,包括五轮面试的详细内容和经验总结。其中四轮为技术面试,一轮为HR面试,涵盖了大量的Java技术和项目实践经验。 ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • 本文详细探讨了如何通过分析单个或多个线程在瓶颈情况下的表现,来了解处理器资源的消耗。无论是单进程还是多进程环境,监控关键指标如线程数量、占用时间及调度优先级等,有助于揭示潜在的性能问题。 ... [详细]
  • 在高并发需求的C++项目中,我们最初选择了JsonCpp进行JSON解析和序列化。然而,在处理大数据量时,JsonCpp频繁抛出异常,尤其是在多线程环境下问题更为突出。通过分析发现,旧版本的JsonCpp存在多线程安全性和性能瓶颈。经过评估,我们最终选择了RapidJSON作为替代方案,并实现了显著的性能提升。 ... [详细]
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社区 版权所有