设计模式 创建型 单例模式(Singleton Pattern)与 常见技术框架应用 解析

news2025/1/6 23:55:27

在这里插入图片描述

单例模式(Singleton Pattern)是一种创建型设计模式,旨在确保某个类在应用程序的生命周期内只有一个实例,并提供一个全局访问点来获取该实例。这种设计模式在需要控制资源访问、避免频繁创建和销毁对象的场景中尤为有用。

一、核心思想

单例模式的核心思想是限制类的实例化次数,确保整个应用程序中只有一个实例存在,并且这个实例可以被全局访问。这通常通过私有化构造函数、提供一个静态方法来获取实例的方式实现。

  1. 唯一性:保证一个类在系统中只有一个实例。
  2. 控制访问:提供一个全局访问点来获取该实例。
  3. 延迟初始化(可选):实例化可以延迟到第一次使用时,以节省资源。
实现单例模式的关键要素:
  • 私有构造函数:防止外部通过new关键字创建新的实例。
  • 静态方法或属性:用于存储和返回唯一的实例。
  • 线程安全:如果应用是多线程的,则需要确保在并发环境下也能正确工作。

二、定义与结构

定义:单例模式确保一个类只有一个实例,并提供一个全局访问点来获取该实例。

结构

  • 单例类:负责创建和管理唯一的实例。
  • 静态成员变量:用于保存唯一的实例引用。
  • 获取实例方法:通常是一个静态方法,用于返回唯一的实例。
  • 私有构造函数:防止外部通过构造函数创建多个实例。
角色
  • 单例类:包含私有构造函数、静态成员变量和获取实例的静态方法。

三、实现步骤及代码示例

以Java为例,单例模式的实现方式有多种,包括饿汉式、懒汉式、双重检查锁定(Double-Checked Locking)和静态内部类实现方式等。以下是几种常见实现的代码示例:

1、饿汉式

  • 特点:在类加载时就创建实例,没有延迟加载的效果,但避免了多线程的同步问题。
  • 优点:线程安全,执行效率高。
  • 缺点:可能导致内存浪费,因为实例在类加载时就已创建,即使未使用。
public class HungrySingleton implements Serializable {
    private static final long serialVersionUID = 1L;
    private static final HungrySingleton hungry = new HungrySingleton();

    private HungrySingleton() {
        // 防止反序列化时重新创建实例
        if (hungry != null) {
            throw new RuntimeException("请使用 HungrySingleton.getInstance() 方法获取一个单例实例");
        }
    }

    public static HungrySingleton getInstance() {
        return hungry;
    }

    // 其他方法...
}

2、懒汉式(线程不安全)

  • 特点:按需加载,节省资源,只有在确实需要的时候才会创建实例。但存在线程安全问题。

  • 优点:延迟加载,提高了程序启动的速度。

  • 缺点:在多线程环境下需要额外的同步机制来保证线程安全。

  • 线程不安全的懒汉式示例代码:

public class LazySingleton {
    private static LazySingleton lazyMan;

    public LazySingleton() {
        // 构造函数可以为空或包含初始化代码
    }

    public static LazySingleton getInstance() {
        if (lazyMan == null) {
            lazyMan = new LazySingleton();
        }
        return lazyMan;
    }

    // 其他方法...
}

上述代码在多线程环境下可能会出现多个实例,因此线程不安全。

注意:懒汉式(线程不安全)在多线程环境下可能会导致多个实例被创建,因此通常不推荐使用。

3、双重检查锁定(Double-Checked Locking)

线程安全的懒汉式示例代码(双重检查锁定):

public class LazySafe {
    private static volatile LazySafe instance = null;
    private LazySafe() {}
    public static LazySafe getInstance() {
        if (instance == null) {
            synchronized (LazySafe.class) {
                if (instance == null) {
                    instance = new LazySafe();
                }
            }
        }
        return instance;
    }
}

使用volatile关键字确保在多线程环境中正确处理,双重检查锁定保证了线程安全和性能。

4、静态内部类实现方式

  • 特点:利用Java的类加载机制实现延迟加载,线程安全且高效。

  • 优点:实现简单,无需额外的同步机制。

  • 缺点:无法支持非静态单例需求。

  • 示例代码:

public class InnerClassSingleton {
    private InnerClassSingleton() {
        // 构造函数可以为空或包含初始化代码
    }

    private static class Holder {
        private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
    }

    public static InnerClassSingleton getInstance() {
        return Holder.INSTANCE;
    }

    // 其他方法...
}

5、枚举单例

  • 特点:实现简单,线程安全,防止反射和序列化破坏。

  • 优点:最简洁的实现方式,由Java语言本身保证线程安全性。

  • 缺点:不支持延迟加载。

  • 示例代码:

public enum Singleton {
    INSTANCE;
    public void someMethod() {
        // 单例方法逻辑
    }
}

四、JavaScript中的单例模式实现

基本实现

class Singleton {
    constructor() {
        if (typeof Singleton.instance === 'object') {
            return Singleton.instance;
        }
        Singleton.instance = this;
    }

    static getInstance() {
        if (!this.instance) {
            new this();
        }
        return this.instance;
    }

    someBusinessLogic() {
        // ...业务逻辑...
    }
}

// 使用示例
const instanceA = Singleton.getInstance();
const instanceB = Singleton.getInstance();

console.log(instanceA === instanceB); // true

模块模式下的单例

在JavaScript中,模块本身就是一个天然的单例,因为每个模块只会被加载一次。因此,可以利用ES6模块来实现单例模式。

// singleton.js
const singleton = (() => {
    let privateState = {}; // 私有状态

    function somePrivateMethod() {
        // 私有方法
    }

    return {
        publicMethod: function() {
            // 公共方法
            somePrivateMethod();
        },
        getPrivateState: function() {
            return privateState;
        }
    };
})();

export default singleton;

// 在其他文件中导入并使用
import singleton from './singleton';

singleton.publicMethod();
console.log(singleton.getPrivateState());

线程安全的单例(适用于Node.js)

当涉及到多线程环境时,如Node.js worker_threads,可能需要确保线程安全。

class ThreadSafeSingleton {
    constructor() {
        if (!ThreadSafeSingleton.instance) {
            ThreadSafeSingleton.instance = this;
        }
        return ThreadSafeSingleton.instance;
    }

    static getInstance() {
        if (!this.instance) {
            // 如果是在多线程环境中,这里应该加入锁机制
            // 例如使用Promise或其他同步机制来保证线程安全
            new this();
        }
        return this.instance;
    }

    someBusinessLogic() {
        // ...业务逻辑...
    }
}

使用立即执行函数表达式(IIFE)

这是一种经典的JavaScript单例实现方式,尤其是在不支持模块化的旧版本浏览器中。

const singleton = (function () {
    const privateState = {}; // 私有状态

    function privateMethod() {
        // 私有方法
    }

    return {
        publicMethod: function () {
            // 公共方法
            privateMethod();
        },
        getPrivateState: function () {
            return privateState;
        }
    };
})();

五、常见技术框架应用

在Spring框架中,单例模式也得到了广泛应用。Spring容器默认创建的Bean是单例的,即在整个Spring IoC容器中,一个Bean只会有一个实例。以下是Spring中配置单例Bean的示例:

<bean id="myBean" class="com.example.MyBean" singleton="true"/>

或者,在基于注解的配置中,可以通过@Component@Bean注解来定义Bean,并默认其为单例:

@Component
public class MyBean {
    // ...
}

@Configuration
public class AppConfig {
    @Bean
    public MyBean myBean() {
        return new MyBean();
    }
}

六、应用场景

单例模式常用于以下场景:

  1. 日志记录器:在整个应用程序中,通常只需要一个日志记录器来记录日志信息。
  2. 配置管理器:应用程序的配置信息通常只需要一个实例来管理,以确保配置的一致性。
  3. 数据库连接池:为了有效地管理数据库连接,避免频繁地创建和销毁连接,通常使用单例模式来创建数据库连接池。
  4. 线程池:管理和复用线程,避免频繁地创建和销毁线程,提高系统性能。
  5. 全局计数器:在需要全局唯一的计数器时,可以使用单例模式。
  6. 购物车服务:在电子商务网站中,用户的购物车应当是唯一的。
  7. 窗口管理器:图形界面程序中,窗口管理器应确保只有一个实例来协调所有窗口的行为。

七、优缺点

优点

  1. 延迟加载:只有在需要时才创建实例,节省资源。
  2. 全局访问点:提供一个全局访问对象的方式,方便在不同模块和组件之间共享资源。
  3. 控制资源:在需要限制实例数量的场景下(如数据库连接池、日志系统),单例模式能够确保系统中只有一个实例在操作资源。

缺点

  1. 线程安全问题:在多线程环境下,需要额外的同步机制来确保线程安全,可能会影响性能。
  2. 单例类的职责单一:单例模式通常要求单例类只承担一个职责,否则可能会违背单一职责原则,导致代码难以维护。

综上所述,单例模式是一种非常有用的设计模式,在需要控制资源访问和避免频繁创建对象的场景中发挥着重要作用。然而,在使用时也需要注意其潜在的缺点,并根据具体场景选择合适的实现方式。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2271175.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

290-3U VPX i7 刀片计算机

一、产品概述 该产品是一款基于第三代Intel i7双核四线程的高性能3U VPX刀片式计算机。产品提供了多个高速PCIe总线接口&#xff0c;其中3个x4 PCIe 3.0接口&#xff0c;1个x4 PCIe 2.0接口。x4 PCIe 2.0接口可灵活配置为4个x1 PCIe接口&#xff0c;因此产品具有很强的扩展性&a…

【从零开始入门unity游戏开发之——C#篇41】C#迭代器(Iterator)——自定义类实现 foreach 操作

文章目录 前言一、什么是迭代器&#xff1f;二、标准迭代器的实现方法1、自定义一个类CustomList2、让CustomList继承IEnumerable接口3、再继承IEnumerator接口4、完善迭代器功能5、**foreach遍历的本质**&#xff1a;6、在Reset方法里把光标复原 三、用yield return语法糖实现…

win32汇编环境,对话框程序中通过资源显示bmp图像

;运行效果 ;win32汇编环境,对话框程序中通过资源显示bmp图像 ;通过资源的方式&#xff0c;会把图像固定在exe文件里&#xff0c;会变大。通过读取文件的方式&#xff0c;没有固定在exe文件里&#xff0c;也可以随时换图像文件&#xff0c;所以exe文件较小 ;直接抄进RadAsm可编译…

MATLAB画柱状图

一、代码 clear; clc; figure(position,[150,100,900,550])%确定图片的位置和大小&#xff0c;[x y width height] %准备数据 Y1[0.53,7.9,8.3;0.52,6.8,9.2;0.52,5.9,8.6;2.8,5.8,7.9;3.9,5.2,7.8;1.8,5.8,8.4]; % withoutNHC X11:6; %画出4组柱状图&#xff0c;宽度1 h1…

java 搭建一个springboot3.4.1项目 JDK21

环境准备 idea:2021 springboot:3.4.1 JDK:21 maven:3.6.3 新建项目 点击new->project->spring initializr 选择springboot版本 1.选择springboot版本&#xff0c;因为JDK版本是21因此对应springboot3.X Spring Boot 2.6.x&#xff1a;适用于JDK 8到17&#xff0c…

第R3周:RNN-心脏病预测

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 文章目录 一、前言二、代码流程1、导入包&#xff0c;设置GPU2、导入数据3、数据处理4、构建RNN模型5、编译模型6、模型训练7、模型评估 电脑环境&#xff1a;…

40% 降本:多点 DMALL x StarRocks 的湖仓升级实战

小编导读&#xff1a; 多点 DMALL 成立于2015年&#xff0c;持续深耕零售业&#xff0c;为企业提供一站式全渠道数字零售解决方案 DMALL OS。作为 DMALL OS 数字化能力的技术底座&#xff0c;大数据平台历经多次迭代平稳支撑了公司 To B 业务的快速开展。随着国家产业升级和云原…

Docker 环境中搭建 Redis 哨兵模式集群的步骤与问题解决

在 Docker 环境中搭建 Redis 哨兵模式集群的步骤与问题解决 在 Redis 高可用架构中&#xff0c;哨兵模式&#xff08;Sentinel&#xff09;是确保 Redis 集群在出现故障时自动切换主节点的一种机制。通过使用 Redis 哨兵&#xff0c;我们可以实现 Redis 集群的监控、故障检测和…

华为消费级QLC SSD来了

近日&#xff0c;有关消息显示&#xff0c;华为的消费级SSD产品线&#xff0c;eKitStor Xtreme 200E系列&#xff0c;在韩国一家在线零售商处首次公开销售&#xff0c;引起了业界的广泛关注。 尽管华为已经涉足服务器级别的SSD制造多年&#xff0c;但直到今年6月才正式推出面向…

StableDiffusionWebUI本地部署指南(WIN)

最近接手了一个需要使用 Stable Diffusion 的项目&#xff0c;要重新部署一套 SD 环境。这跟我之前的SD部署又不太一样&#xff0c;部署过程中遇到一些问题&#xff0c;总结出一个比较完美的安装方案&#xff0c;在这里和大家分享一下。 项目地址&#xff1a;https://github.c…

运动控制探针功能详细介绍(CODESYS+SV63N伺服)

汇川AM400PLC和禾川X3E伺服EtherCAT通信 汇川AM400PLC和禾川X3E伺服EtherCAT通信_汇川ethercat通信-CSDN博客文章浏览阅读1.2k次。本文详细介绍了如何使用汇川AM400PLC通过EtherCAT总线与禾川X3E伺服进行通信。包括XML硬件描述文件的下载与安装,EtherCAT总线的启用,从站添加…

ELK日志平台搭建 (最新版)

一、安装 JDK 1. 下载 JDK 21 RPM 包 wget https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.rpm2. 安装 JDK 21,使用 rpm 命令安装下载的 RPM 包&#xff1a; sudo rpm -ivh jdk-21_linux-x64_bin.rpm3. 配置环境变量 编辑 /etc/profile 文件以配置 JAVA_HO…

pygame飞机大战

飞机大战 1.main类2.配置类3.游戏主类4.游戏资源类5.资源下载6.游戏效果 1.main类 启动游戏。 from MainWindow import MainWindow if __name__ __main__:appMainWindow()app.run()2.配置类 该类主要存放游戏的各种设置参数。 #窗口尺寸 import random import pygame WIND…

应用架构模式

设计模式 设计模式是指根据通用需求来设计解决方案的模板或蓝图&#xff0c;使用设计模式能够更加有效地解决设计过程中的常见问题。设计模式针对不同的问题域有不同的内涵&#xff0c;主要涉及业务、架构、程序设计等问题域&#xff0c;本文主要讨论架构设计模式。 业务设计模…

el-input输入框需要支持多输入,最后传输给后台的字段值以逗号分割

需求&#xff1a;一个输入框字段需要支持多次输入&#xff0c;最后传输给后台的字段值以逗号分割 解决方案&#xff1a;结合了el-tag组件的动态编辑标签 那块的代码 //子组件 <template><div class"input-multiple-box" idinputMultipleBox><div>…

【新教程】华为昇腾NPU的pytorch环境搭建

1 硬件配置 使用学校的集群&#xff0c;相关配置如下&#xff1a; CPU&#xff1a;鲲鹏920 NPU&#xff1a;昇腾910B 操作系统&#xff1a;openEuler 22.03 2 安装版本 根据昇腾官方gitee上的信息&#xff0c;Pytoch 2.1.0是长期支持版本&#xff0c;因此选择安装这一版本&a…

游戏引擎学习第72天

无论如何&#xff0c;我们今天有一些调试工作要做&#xff0c;因为昨天做了一些修改&#xff0c;结果没有时间进行调试和处理。我们知道自己还有一些需要解决的问题&#xff0c;却没有及时完成&#xff0c;所以我们想继续进行这些调试。对我们来说&#xff0c;拖延调试工作总是…

信号的产生、处理

一、信号的概念 信号是linux系统提供的一种&#xff0c;向指定进程发送特定事件的方式。收到信号的进程&#xff0c;要对信号做识别和处理。信号的产生是异步的&#xff0c;进程在工作过程中随时可能收到信号。 信号的种类分为以下这么多种&#xff08;用指令kill -l查看&…

Node.js应用程序遇到了内存溢出的问题

vue 项目 跑起来&#xff0c;一直报错&#xff0c;内存溢出 在 文件node_modules 里 .bin > vue-cli-service.cmd 在依赖包这个文件第一行加上这个 node --max-old-space-size102400 "%~dp0\..\vue\cli-service\bin\vue-cli-service.js" %* node --max-old-s…

openGauss与GaussDB系统架构对比

openGauss与GaussDB系统架构对比 系统架构对比openGauss架构GaussDB架构 GaussDB集群管理组件 系统架构对比 openGauss架构 openGauss是集中式数据库系统&#xff0c;业务数据存储在单个物理节点上&#xff0c;数据访问任务被推送到服务节点执行&#xff0c;通过服务器的高并…