【只生一个好 - 单例设计模式(Singleton Pattern)】

单例设计模式

  • 单例设计模式(Singleton Pattern)
    • talk is cheap, show you my code
      • 饿汉式
      • 懒汉式
      • 双重检查锁定
      • 静态内部类
      • 枚举
    • 总结

单例设计模式(Singleton Pattern)

单例设计模式(Singleton Pattern)是面向对象编程中的一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一的实例。这种模式在需要控制资源的访问或配置时非常有用,例如数据库连接池、线程池、缓存等场景。

太抽象了
来我们换个说法,当一些资源比较重的时候,我们不希望创建那么多的实例。举个现实的例子,我们有一大堆地方法院;但是最高法院只有一个。为什么? 因为当所有的事情有分歧的时候,一层层上报,总是需要有一个部门有最终决策权。

talk is cheap, show you my code

饿汉式

饿汉式是在类加载时就创建好实例,这种方式简单直接,但缺点是即使实例从未被使用过,也会占用内存资源。

public class Singleton {

    private static Singleton singleton = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return singleton;
    }
}

我们通过将Singleton设置为private,使得外部没有办法调用构造方法新建实例;又利用类加载机制,我们将Singleton设置为静态变量,这样的话,类加载的时候,就可以新建一个实例;最后开放一个静态方法getInstance()返回实例。

懒汉式

懒汉式是在第一次调用 getInstance() 方法时才创建实例,这样可以节省资源,但是如果多线程环境下未加锁可能会导致多个实例被创建。

public class Singleton1 {
    private static Singleton1 singleton = null;

    private Singleton1() {
    }

    public static Singleton1 getInstance() {
        if (singleton == null) {
            singleton = new Singleton1();
        }
        return singleton;
    }
}

我们通过将Singleton设置为private,使得外部没有办法调用构造方法新建实例;然后调用getInstance()的时候触发新建实例的动作,返回实例,但是这个代码在多线程下可能会创建出多个实例。

双重检查锁定

上面的懒汉式单例写入有多线程的安全问题,为了保证线程安全同时避免不必要的同步开销,可以采用双重检查锁定的方式。

public class Singleton1 {
    private volatile static Singleton1 singleton = null;

    private Singleton1() {
    }

    public static Singleton1 getInstance() {
        if (singleton == null) {
            synchronized (Singleton1.class) {
                if (singleton == null) {
                    singleton = new Singleton1();
                }
            }
        }
        return singleton;
    }
}

synchronized这个关键字用来加锁,但是仅仅通过synchronized也不能保证只产生一个实例,为什么呢?
因为第一个 if (singleton == null)后面通过synchronized拿锁,然后有一个线程会拿到锁,另一些在等待锁,如果不写第二个if判断的话,导致第一个拿到锁的执行了创建实例的操作,释放锁。而等待锁的线程就可以拿到锁,导致又创建了新的实例,所以我们要写两个if判断。所以这种创建单例的写法又叫做双重检查锁定。

静态内部类

public class Singleton {
    private Singleton() {}

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

利用 Java 的类加载机制,在第一次调用 getInstance() 方法时才会加载静态内部类,从而实现延迟初始化和线程安全。换言之,这种的线程安全是通过类加载机制保证的,我们就不需要再写线程安全控制了。
另外补充private static final Singleton INSTANCE = new Singleton();这个看起来像是饿汉式,但是实际上只有调用getInstance的时候才会创建对象,因为SingletonHolder是在它自己被调用的时候才会触发类加载。所以从创建时机上看,这种是懒汉式一样的。

枚举

枚举天生就是线程安全的,并且可以防止反序列化攻击,因此它是一种非常简洁且推荐的方式来实现单例模式。

public enum Singleton {
    INSTANCE;

    // 单例对象的成员变量
    private String data;

    // 私有构造函数
    private Singleton() {
        this.data = "Initial Data";
    }

    // 获取数据的方法
    public String getData() {
        return data;
    }

    // 设置数据的方法
    public void setData(String data) {
        this.data = data;
    }

    // 其他方法
    public void doSomething() {
        System.out.println("Doing something with data: " + data);
    }

    // 主方法用于测试
    public static void main(String[] args) {
        // 获取单例实例
        Singleton singleton = Singleton.INSTANCE;

        // 使用单例实例的方法
        System.out.println(singleton.getData()); // 输出: Initial Data
        singleton.setData("New Data");
        System.out.println(singleton.getData()); // 输出: New Data
        singleton.doSomething(); // 输出: Doing something with data: New Data
    }
}

总结

单例模式特点:

  1. 单例模式的优点
  • 唯一性:确保了类在整个应用程序中只有一个实例。
  • 控制资源:对于需要严格控制资源使用的场合非常适合。
  • 延迟加载:可以通过某些实现方式(如懒汉式、静态内部类)实现延迟初始化,节省资源。
  • 线程安全:通过适当的实现方式可以保证在多线程环境下的安全性。
  1. 单例模式的缺点
  • 难以测试:单例模式引入了全局状态,这可能使得单元测试变得困难。
  • 隐藏依赖关系:因为单例模式通常通过静态方法提供实例,所以依赖关系往往被隐藏起来,不利于依赖注入和代码维护。
  • 不适用于分布式系统:在一个分布式的环境中,单例模式无法保证跨进程或跨机器的唯一性。

单例模式应用场景

  • 配置管理:如读取配置文件或环境变量。
  • 日志记录器:整个应用共享同一个日志记录器实例。
  • 工厂类:如工厂模式中的工厂类,确保所有客户端都使用同一个工厂来创建对象。
  • 数据库连接池:控制对数据库连接的访问,确保高效利用有限的连接资源。

单例模式是一个简单而强大的设计模式,广泛应用于各种需要确保唯一实例的场景。Spring 中的Bean默认就是单例模式的。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

邮箱手机号脱敏

项目场景: 提示:这里简述项目相关背景: 输入框的脱敏,当输入的时候显示正常,失去焦点部分显示**** 问题描述 提示:这里描述项目中遇到的问题: 脱敏可以封装 一下成为一个方法,挂…

基于Oauth2的SSO单点登录---前端

Vue-element-admin 是一个基于 Vue.js 和 Element UI 的后台管理系统框架,提供了丰富的组件和功能,可以帮助开发者快速搭建现代化的后台管理系统。 一、基本知识 (一)Vue-element-admin 的主要文件和目录 vue-element-admin/ |--…

【Artificial Intelligence篇】AI 携手人类:共铸未来创作新纪元

引言: 随着科技的飞速发展,人工智能已逐渐渗透到各个领域,尤其是在创作领域,其与人类的合作展现出了前所未有的可能性和潜力。从艺术作品的生成到文学作品的创作,从复杂软件的开发到创新设计的构思,AI 正在…

Easy-Trans反向翻译+Excel导入最佳实践

1、概述 实现用户excel上传、解析、对于用户输入的中文翻译为字典码或者id,实现用户输入的参数校验,最后入库。如果用户输入的参数有问题,返回校验结果给前端。 excel解析使用My-Excel组件,校验使用hibernate-validator&#xff…

OpenCV-Python实战(6)——图相运算

一、加法运算 1.1 cv2.add() res cv2.add(img1,img2,dstNone,maskNone,dtypeNone) img1、img2:要 add 的图像对象。(shape必须相同) mask:图像掩膜。灰度图(维度为2)。 dtype:图像数据类型…

Leetcode打卡:查询数组中元素出现的位置

执行结果:通过 题目 3159 查询数组中元素出现的位置 给你一个整数数组 nums ,一个整数数组 queries 和一个整数 x 。 对于每个查询 queries[i] ,你需要找到 nums 中第 queries[i] 个 x 的位置,并返回它的下标。如果数组中 x 的出…

向量组学习

向量组的秩及其线性组合 线性相关性 先看a1,a2 如果这两个向量不对应成比例的话,那必然内部不可能存在多余的向量,也就是无关. 主元所在的列都是独立向量 ,最大无关组就是b1,b2,b4,但这个是初等行变换后的,题目要的是A的,与之对应的就是a1,a2,a4 方程组解的结构

影视仓最新接口+内置本包方法的研究(2024.12.27)

近日喜欢上了研究影视的本地仓库内置,也做了一个分享到了群里。 内置本地仓库包的好处很明显,当前线路接口都是依赖网络上的代码站存放,如果维护者删除那就GG。 虽然有高手制作了很多本地包,但推送本地包到APP,难倒一片…

redis相关数据类型介绍

当然,Redis 作为一个高性能的键值存储系统,提供了多种数据类型来支持不同的应用场景。 1. String(字符串) • 定义:Redis 最基本的数据类型,用于存储字符串值。 • 操作:SET、GET、INCR、DECR、…

教师管理系统

大概功能: 1.显示所有教师 2.按姓名查找教师 3.按工号查找教师 4.增加教师 5.删除教师 6.退出 数据会保存到 txt 文件里面 姓名:必须是中文 手机号码:必须是11位,必须是数字 效果展示: 代码展示: Teache…

lombok-macros

GITHUB 地址 LTPP-GIT 地址 官方文档 API 文档 一组提供 Lombok 类似功能的 Rust 宏。 安装 要使用此 crate,可以运行以下命令: cargo add lombok-macros用法 use lombok_macros::*;/// 定义一个结构体,使用 Lombok 宏派生所需的方法 #…

uniapp开发微信小程序实现获取“我的位置”

1. 创建GetLocation项目 使用HBuilder X创建一个项目GetLocation,使用Vue3。 2. 在腾讯地图开放平台中创建应用 要获取位置,在小程序中需要使用腾讯地图或是高德地图。下面以腾讯地图为例。 (1)打开腾讯地图开放平台官方网址:腾讯位置服务 - 立足生态,连接未来 (2)注册…

Docker基础知识 Docker命令、镜像、容器、数据卷、自定义镜像、使用Docker部署Java应用、部署前端代码、DockerCompose一键部署

目录 1.Docker 2.镜像和容器 2.1 定义 2.2 开机自动启动容器 3.docker命令 3.1 docker run 参数说明 3.2 常见命令 3.3 命令演示 3.4 命令别名 4.Docker命令详解 5.数据卷 5.1 定义 5.2 数据卷的相关命令 5.3 数据卷命令 5.4 挂载本地目录或文件 5.4.1 定义 5.4.2 mysql容器目录…

Linux | Ubuntu零基础安装学习cURL文件传输工具

目录 介绍 检查安装包 下载安装 手册 介绍 ‌cURL是一个利用URL语法在命令行下工作的文件传输工具,首次发行于1997年‌‌12。cURL支持多种协议,包括FTP、FTPS、HTTP、HTTPS、TFTP、SFTP、Gopher、SCP、Telnet、DICT、FILE、LDAP、LDAPS、IMAP、POP3…

c# 2024/12/27 周五

6《详解类型、变量与对象》36 详解类型、变量与对象 _1_哔哩哔哩_bilibili

yarn list --pattern vuex-module-decorators

dgqdgqdeMac-mini spid-admin % yarn list --pattern vuex-module-decorators yarn list v1.22.22 └─ vuex-module-decorators0.16.1 ✨ Done in 0.24s.好的,这段代码是一个典型的 Vuex 模块定义,使用了 vuex-module-decorators 库。这个库为 Vuex 提…

uniapp 判断多选、选中取消选中的逻辑处理

一、效果展示 二、代码 1.父组件: :id=“this.id” : 给子组件传递参数【id】 @callParentMethod=“takeIndexFun” :给子组件传递方法,这样可以在子组件直接调用父组件的方法 <view @click="$refs.member.open()"

IDEA自己常用的几个快捷方式(自己的习惯)

TOC 背景 换工作了, 新的IDEA, 又要重新设置自己的快捷方式了. 灵感 1.这些个性话的配置应该是可以导出的. 然后在新的IDEA直接导入就行了, 感觉应该是有这个功能. 就是这个文件: <keymap version"1" name"Personal KeyMap" parent"$default…

学习AndroidPerfetto基础一

1.哔哩哔哩学习视频&#xff1a; Android Perfetto 基础和案例分享_哔哩哔哩_bilibili 2.Perfetto的简单介绍 Perfetto 是一个用于性能检测进而追踪分析的生产级开源工具 Perfetto提供上帝视角&#xff0c;背后需要整个Android系统的知识储备 Perfetto由Google开发&#x…

ffmpeg: stream_loop报错 Error while filtering: Operation not permitted

问题描述 执行ffmpeg命令的时候&#xff0c;报错&#xff1a;Error while filtering: Operation not permitted 我得命令如下 ffmpeg -framerate 25 -y -i /data/workerspace/mtk/work_home/mtk_202406111543-l9CSU91H1f1b3/tmp/%08d.png -stream_loop -1 -i /data/workerspa…