设计模式之单例模式详解

news2024/12/30 2:41:47

单例模式

描述:单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。

核心特点

  1. 单例类只有一个实例对象
  2. 该单例对象必须由单例类自行创建
  3. 单例类对外提供一个访问该单例的全局访问点

实现方式

​ 通常,普通类的构造函数是公有的,外部类可以通过“new 构造函数()”来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。

角色功能

  • 单例类:包含一个实例且能自行创建这个实例的类。
  • 访问类:使用单例的类。

类图:
请添加图片描述

优点:

  • 单例模式可以保证内存里只有一个实例,避免重复创建和销毁,减少了内存的开销
  • 可以避免对资源的多重占用。
  • 单例模式设置全局访问点,可以优化和共享资源的访问。

缺点:

  • 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
  • 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
  • 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。

实现代码

实现要点:private修饰默认构造函数、private static修饰实例对象、public static修饰获取实例的方法

懒汉式单例

该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例,第一次用到才创建实例,所以叫懒汉(这其实是优点)。

特点:

  • 实现简单
  • 首次使用时才创建对象实例(实现了懒加载)
  • 若想实现线程安全则必须使用synchronized,效率较低
public class LazySingleton {
    private static volatile LazySingleton instance = null;    //保证 instance 在所有线程中同步

    private LazySingleton() {
    }    //private 避免类在外部被实例化

    public static synchronized LazySingleton getInstance() {
        //getInstance 方法前加同步
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

双重校验机制

属于懒汉式的拓展实现

特点:

  • 能实现懒加载,也是线程安全的,且效率较高
  • 实现复杂,需要两次校验(进入同步块后还要校验一次)
public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
            if (singleton == null) {  
                singleton = new Singleton();  
            }  
        }  
    }  
    return singleton;  
    }  
}

问:为什么synchronized语句不把第一个if判断语句要包括进去?

答:包括进去就和synchronized方法没区别了,效率很低。

问:为什么synchronized语句块创建实例对象前还要再判断一次实例是否被创建?

答:为了保证实例只被创建一次。如果初始实例为null时,有多个线程进入到第一个判断语句并竞争锁,拿到锁的线程如果已经创建了实例后,后续拿到锁的线程就不能再创建了,所以要再判断一次。换句话说synchronized去修饰实例创建语句时候,只能保证同时刻只有一个在创建,而不能保证后续没有别的线程去创建。

饿汉式单例

该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了,它是线程安全的,可以直接用于多线程而不会出现问题。

特点:

  • 类加载时就创建好对象实例,也就是不管是否用到都初始化,浪费内存
  • 天然的线程安全,不需要使用到锁
public class HungrySingleton {
    //类加载时就会创建实例化对象
    private static HungrySingleton instance = new HungrySingleton();
    
    private HungrySingleton() {
    }

    public static HungrySingleton getInstance() {
        return instance;
    }
}

静态内部类

属于饿汉式的拓展,解决饿汉式无法实现懒加载问题,能实现和双重校验机制一样的效果。

在单例类里面创建一个SingletonHolder内部静态类,外部的单例类被加载时不会立马初始化内部静态类,只有去调用内部静态类的静态成员或静态方法才会触发类的加载,也能够实现第一次用到时才加载,也就是懒加载。

public class Singleton {  
    //内部静态类,单例类加载时不会立马初始化,只有访问内部静态变量时才初始化
    private static class SingletonHolder {  
    	private static Singleton INSTANCE = new Singleton();  
    }  
    //空的构造方法
    private Singleton (){
        
    }  
    public static Singleton getInstance() {  
        return SingletonHolder.INSTANCE;  
    }  
}

枚举式

单例模式的最佳方法,非常简洁、高效、线程安全、支持序列化机制、无法被反射机制破解(其它方式都可以)。

    public enum Singleton {
        INSTANCE;  // 枚举里的属性相当于Singleton的实例
        private Person instance;

        Singleton() {// 私有构造函数 默认是 private
            instance = new Person(); // 在构造函数中完成实例化操作
        }

        public static Person getInstance() {// 提供公有方法对其访问
            return instance;
        }
    }
    // 在外部使用Singleton.INSTANCE.getInstance();来调用
}

如何选?

  • 懒汉式要么线程不安全,要么效率低,要么实现复杂,不建议使用。
  • 如果单例对象占用资源大,需要懒加载,建议用内部静态类方式(优于懒汉式)。
  • 如果单例对象占用资源小,不需要懒加载,建议用枚举式(优于饿汉式)。

应用场景

对于 Java来说,单例模式可以保证在一个 JVM 中只存在单一实例。单例模式的应用场景主要有以下几个方面。

  • 某类需要频繁创建销毁,且资源消耗又比较大的对象,如线程池数据库连接池、配置文件、日志管理等,单例可提高性能和节省资源。
  • 当对象需要多线程共享访问的场合。由于单例模式只允许创建一个对象,能够保证线程安全性且达到共享目的。
  • 外部和内部资源管理。对于管理外部打印机、回收站内部属性文件的系统,单例模式可以确保这些资源被系统中的唯一实例所管理。
  • 对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套。

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

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

相关文章

Hive SQL-DML-insert插入数据

Hive SQL-DML-insert插入数据 1. 插入静态数据 可以直接插入具体的值到Hive表中: INSERT INTO TABLE tablename (column1, column2, column3) VALUES (value1, value2, value3),(value4, value5, value6),...;2. 插入查询结果 将一条查询的结果直接插入到另一个表中…

webpack从零到1 构建 vue3

为什么要手写webpack 不用cli (无的放矢)并不是 其实是为了加深我们对webpack 的了解方便以后灵活运用webpack 的技术 初始化项目结构(跟cli 结构保持一致) 新建 public src 等文件夹npm init -y 创建package.json文件tsc --init…

量化教程3---miniqmt当作第三方库设置,提供源代码

qmt提供了大qmt和miniqmt,大qmt在平台使用,miniqmt提供了交易的api和数据可以本地使用,非常的方便,合适自己开发大型的策略,本地还可以访问其他数据,网络等,也支持服务器 以前的教程 qmt教程1…

电脑怎么压缩视频?win端、Mac端压缩工具分享~

我们经常需要处理和分享视频文件。然而,视频文件往往会占用大量的存储空间,特别是高分辨率和高质量的视频。为了方便存储和分享,我们常常需要将视频文件进行压缩。本文将介绍如何使用电脑系统win端或Mac端自带的视频编辑器、以及常用的剪辑软…

树莓派配置双网卡分别为AD HOC和AP模式

树莓派配置双网卡分别为AD HOC和AP模式 需求说明:为了实现分级网络管理,将多个无人机分簇,簇间使用AD HOC进行无中心自组织的网络,簇内使用AP-AC模式进行中心化网络。因此,需要配置一台设备,同时完成AD HOC…

初识指针(3)<C语言>

前言 前面两篇文章已经介绍了一些关于指针的基础知识,下面我们可以涉及一些指针较容易混淆的概念,本篇文章将介绍数组名的理解、指针输入打印数组的不同格式、一维数组传参的本质,冒泡排序,二级指针,指针数组等。 数组…

【BST】Behavior Sequence Transformer for E-commerceRecommendation in Alibaba

一、提出背景 传统的Embedding&MLP模型结构将原始特征嵌入到低维向量中,然后将其concat后输入MLP进行最终推荐。DIN提出使用注意力机制来捕获候选项与用户先前点击的项之间的相似性。 然而,大多数这些工作只是连接不同的特征,而没有捕获用…

如何在您的域名中使用 Google Apps 创建 SPF 记录

关于 SPF 记录 SPF 记录是一种域名服务(DNS)记录,用于标识哪些邮件服务器被允许代表您的域发送电子邮件。它与在您的 DNS 区域中添加 MX 或 A 记录一样简单。 为什么它很重要? 如今,几乎所有滥用电子邮件消息都携带…

AMBA总线介绍

AMBA(Advanced Microcontroller Bus Architecture)是由ARM(Advanced RISC Machines)公司设计的一种高性能、高带宽的总线架构。AMBA总线广泛应用于各种嵌入式系统中,包括数字信号处理器、图形处理器、嵌入式处理器以及…

泽众财务RPA机器人常见五个应用场景

泽众RPA(即机器人流程自动化,Robotic Process Automation, RPA)解决方案是依托于各类先进信息技术手段的虚拟劳动力 (数字劳动力),根据预先设定的程序操作指令对任务进行自动化处理,实现业务流程…

QGraphicsView实现简易地图11『指定层级-定位坐标』

前文链接:QGraphicsView实现简易地图10『自适应窗口大小』 提供一个地图初始化函数,指定地图显示的中心点和地图缩放层级 能够让地图显示某一层级的瓦片,并将中心点坐标显示在视图中心。 1、动态演示效果 7级地图-大连-老虎滩 定位到 8级地图…

【Shell】shell编程之条件语句

目录 一、条件测试操作 1.test命令 2.文件测试 3.整数值比较 4.字符串比较 5.逻辑测试 二、if语句的结构 1.单分支结构 2.双分支结构 3.多分支结构 三、case语句 总结 一、条件测试操作 1.test命令 测试表达式是否成立,若成立返回0,否则返回…

Apache DolphinScheduler 4月简报:社区发展与技术革新速递

各位热爱 DolphinScheduler 的小伙伴们,4 月份的 DolphinScheduler 社区月报更新啦!这里将记录 DolphinScheduler 社区每月的重要更新,欢迎关注! 月度 Merge 之星 感谢以下小伙伴 4 月为 Apache DolphinScheduler 所做的精彩贡献…

Graph RAG:基于知识图谱的检索增强技术与优势对比

身处信息爆炸时代,如何从海量信息中获取准确全面的搜索结果,并以更直观、可读的方式呈现出来是大家期待达成的目标。传统的搜索增强技术受限于训练文本数量、质量等问题,对于复杂或多义词查询效果不佳,更无法满足 ChatGPT 等大语言…

spark sql 与scala混合开发实现数据入mongodb

目录 概述资源解决问题效果环境配置相关包关键代码 测试测试结果 概述 在此提供 spark sql 与scala混合开发实现数据入mongodb 相关思路 将部分重复性功能进行通用化(使用SQL与Scala混合开发模式)。 相关组件 hadoop 3.3.6 spark 3.4.2 kyuubi 1.8.0 基于上术组件开发 资源 …

【笔试训练】day22

1.添加字符 求最少不相等的位数,可以先求最多相等的位数。 在添加字符之前,A和B最多相等的位数是多少?由于A后面可以添加字符,也就使得A字符可以在B的任意一个位置开始比较。遍历一遍这个比较的起点,从这个起点开始跟…

Angular中的路由

Angular中的路由 文章目录 Angular中的路由前言一、创建路由二、创建多个组件路由三、创建子路由四、创建多个组件子路由 前言 在Angular中,路由是用于在不同的视图和组件之间导航的机制。Angular提供了一种强大的路由机制来管理单页应用(SPA&#xff0…

Npm Install Docusaurus Demo【npm 安装 docusaurus 实践 】

文章目录 1. 简介2. 前提2.1 安装 git2.2 安装 node 3. 安装4. 项目结构5. 访问5.1 localhost 访问5.2 ip 访问 1. 简介 Docusaurus 是一个facebook的开源项目,旨在帮助开发者构建易于维护和部署的文档网站。它提供了一个简单的方法来创建专业的文档网站&#xff0…

asp.net结课作业中遇到的问题解决3

目录 1、想实现不止鼠标滑过就显示图片,初始化状态下也可以显示图片,且每个图片还会自动变化,该如何实现 2、 同一个项目下的网页之间可以直接在地址栏输入跳转到阅读界面从而实现在这个跳转,那么如何防止这种现象呢?…

python数据分析——pandas DataFrame基础知识1

参考资料:活用pandas库 1、加载数据集 通常调用read_csv函数来加载CSV数据文件。若是.tsv文件也是用read_csv函数。 # 导入库 import pandas as pd # 默认情况下,read_csv函数会读取逗号分隔文件 # Gapminder数据使用制表符分隔 # 可以吧sep参数设置为…