重读 Java 设计模式: 解析单例模式,保证唯一实例的创建与应用

本周工作太忙了,变成了加班狗,下班回来也没时间写,只能利用周末时间写了😭。

好了,言归正传,本次我们先来介绍下设计模式中创建型模式-单例模式

一、引言

单例模式是设计模式中最简单但又最常用的一种模式之一。它确保某个类只能有一个实例,并提供了全局访问点,使得该实例在整个应用程序中都可以被访问。在本文中,我们将深入探讨单例模式的实现方式、应用场景以及实践指南。

二、基本概念

除了引言一段中,单例模式除了实例全局唯一全局访问点外,还有其他特点,下面我将一一罗列出来:

  • 实例全局唯一:单例模式确保一个类只有一个实例对象存在,无论在何处访问该类,都将得到相同的实例。通过单例模式,可以防止不必要的多次实例化,确保系统中某个类只有一个实例存在。
  • 全局访问点:单例模式提供了一个全局的访问点,任何地方都可以通过该访问点获取到单例实例。
  • 延迟加载:许多单例模式的实现方式采用了延迟加载的策略,即在需要时才创建实例对象,而不是在类加载时就创建。
  • 线程安全:好的单例模式实现应该是线程安全的,即在多线程环境下也能够正确地保持单例对象的唯一性。
  • 节约资源:单例模式可以节约系统资源,特别是那些需要频繁创建和销毁的对象,如数据库连接、线程池等。
  • 全局状态管理:单例模式可以用于管理全局的状态信息,例如配置管理器、日志系统等

三、单例模式的五种经典实现案例

3.1 饿汉式单例模式

优点:
  • 线程安全:在类加载时就创建实例对象,因此不存在多线程环境下的线程安全问题。
  • 简单易理解:实现简单,易于理解和使用。
缺点:
  • 不支持延迟加载:在类加载时就创建实例对象,可能会导致资源浪费,尤其是在实例对象较大或者初始化过程较为复杂的情况下。
package com.markus.desgin.mode.creational.singleton;

/**
 * @Author: zhangchenglong06
 * @Date: 2024/3/6
 * @Description: 单例模式一:急切初始化
 */
public class EagerlyInitSingleton {
  // 利用 类加载机制中 static 属性加载特性:全局只加载一遍,不会随实例的初始化重复加载
  private static EagerlyInitSingleton INSTANCE = new EagerlyInitSingleton();

  public static EagerlyInitSingleton getInstance() {
    return INSTANCE;
  }

  public static void main(String[] args) {
    EagerlyInitSingleton eagerlyInitSingleton1 = getInstance();
    EagerlyInitSingleton eagerlyInitSingleton2 = getInstance();

    System.out.println(eagerlyInitSingleton1 == eagerlyInitSingleton2);

  }
}

3.2 懒汉式单例模式

优点:
  • 延迟加载:只有在需要时才创建实例对象,节省了系统资源。
  • 简单易理解:实现简单,易于理解和使用。
缺点:
  • 线程不安全:在多线程环境下,如果多个线程同时调用获取实例的方法,可能会创建多个实例对象,导致单例模式失效。
  • 性能低下:由于使用了 synchronized 关键字进行同步,导致性能较低。
package com.markus.desgin.mode.creational.singleton;

/**
 * @Author: zhangchenglong06
 * @Date: 2024/3/6
 * @Description: 单例模式二: 懒加载模式
 */
public class LazyInitSingleton {
  private static LazyInitSingleton INSTANCE = null;

  public static synchronized LazyInitSingleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new LazyInitSingleton();
    }
    return INSTANCE;
  }

  public static void main(String[] args) {
    LazyInitSingleton singleton1 = getInstance();
    LazyInitSingleton singleton2 = getInstance();

    System.out.println(singleton1 == singleton2);

  }
}

3.3 双重检验锁单例模式

优点:
  • 线程安全:通过双重检验锁机制来确保只有一个实例对象被创建,解决了懒汉式单例模式的线程安全问题。
  • 延迟加载:只有在需要时才创建实例对象,节省了系统资源。
缺点:
  • 实现复杂:双重检验锁的实现较为复杂,需要注意双重检验锁中的原子性问题。
package com.markus.desgin.mode.creational.singleton;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * @Author: zhangchenglong06
 * @Date: 2024/3/6
 * @Description:
 */
public class DoubleCheckLockSingleton {
  // volatile 加入内存屏障机制,将 实例对象的初始化过程 顺序保证原子性
  private static volatile DoubleCheckLockSingleton INSTANCE;

  public static DoubleCheckLockSingleton getInstance() {
    if (INSTANCE == null) {
      // 局部加锁,相比懒汉式,性能更优
      synchronized (DoubleCheckLockSingleton.class) {
        if (INSTANCE == null) {
          // 这个阶段可能会由于 JVM 的性能优化,内部字节码出现乱序执行的情况,所以加上 volatile 关键字
          INSTANCE = new DoubleCheckLockSingleton();
        }
      }
    }
    return INSTANCE;
  }

  public static void main(String[] args) throws ExecutionException, InterruptedException {
    DoubleCheckLockSingleton singleton1 = getInstance();
    DoubleCheckLockSingleton singleton2 = getInstance();

    System.out.println(singleton1 == singleton2);

    CompletableFuture<DoubleCheckLockSingleton> future1 = CompletableFuture.supplyAsync(DoubleCheckLockSingleton::getInstance);
    CompletableFuture<DoubleCheckLockSingleton> future2 = CompletableFuture.supplyAsync(DoubleCheckLockSingleton::getInstance);
    singleton1 = future1.get();
    singleton2 = future2.get();
    System.out.println(singleton1 == singleton2);
  }
}

3.4 静态内部类单例模式

优点:
  • 线程安全:利用类加载机制保证了静态内部类只会被加载一次,从而保证了单例的线程安全性。
  • 延迟加载:只有在需要时才会加载静态内部类,从而实现了延迟加载。
缺点:
  • 不支持传参:静态内部类方式不能支持传递参数给单例的构造函数,因为静态内部类的实例化是由 JVM 保证线程安全的,不能传递参数。
package com.markus.desgin.mode.creational.singleton;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * @Author: zhangchenglong06
 * @Date: 2024/3/6
 * @Description: 单例模式四: 静态内部类
 */
public class StaticInnerClassSingleton {
  private static class Singleton {
    public static StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
  }

  private StaticInnerClassSingleton() {
  }

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

  public static void main(String[] args) throws ExecutionException, InterruptedException {
    StaticInnerClassSingleton singleton1 = getInstance();
    StaticInnerClassSingleton singleton2 = getInstance();

    System.out.println(singleton1 == singleton2);

    CompletableFuture<StaticInnerClassSingleton> future1 = CompletableFuture.supplyAsync(StaticInnerClassSingleton::getInstance);
    CompletableFuture<StaticInnerClassSingleton> future2 = CompletableFuture.supplyAsync(StaticInnerClassSingleton::getInstance);
    singleton1 = future1.get();
    singleton2 = future2.get();
    System.out.println(singleton1 == singleton2);
  }
}

4.4 枚举单例模式

优点:
  • 线程安全:枚举类型是线程安全的,并且只会装载一次,从而保证了单例的线程安全性。
  • 防止反射攻击:枚举类型在反序列化时会检查是否为枚举,从而防止了反射攻击。
缺点:
  • 不支持延迟加载:枚举类型是在类加载时就实例化的,无法实现延迟加载,可能会导致资源浪
package com.markus.desgin.mode.creational.singleton;

/**
 * @Author: zhangchenglong06
 * @Date: 2024/3/6
 * @Description: 单例模式五: 枚举
 */
public enum EnumSingleton {
  INSTANCE;

  public EnumSingleton getInstance(){
    return INSTANCE;
  }
}

单例模式在 Spring 中的应用场景

在 Spring 框架中,所有被 Spring 管理的 Bean 默认都是单例的。Spring 容器负责创建这些 Bean 的唯一实例,并在需要时注入到其他 Bean 中。

例如:

  • 业务 Spring Bean:这种一般是用户自定义的实例,将其定义到 Spring 框架中,并由 Spring 容器进行管理
  • 框架基础设施 Bean:
    • 资源加载器(ResourceLoader):Spring 框架提供了资源加载器来加载各种资源,如配置文件、静态文件等。资源加载器通常也是单例的,在整个应用程序中只存在一个实例,负责加载和管理资源。
    • 事件广播器(ApplicationEventPublisher):Spring 框架提供了事件机制来支持应用程序中的事件处理,而事件发布器用于发布事件通知给感兴趣的监听器。事件发布器通常也是单例的,在整个应用程序中只存在一个实例,负责发布事件通知。
    • 数据源(DataSource):在 Spring 中,数据源用于连接数据库,并提供了对数据库的访问。通常情况下,数据源也是单例的,在整个应用程序中只存在一个实例,负责管理数据库连接。
    • 事务管理器(PlatformTransactionManager):Spring 框架提供了事务管理器来管理事务的提交和回滚。事务管理器通常也是单例的,在整个应用程序中只存在一个实例,负责管理事务的生命周期。
    • 请求分发(DispatcherServlet):DispatcherServlet是Spring MVC框架中的前端控制器,负责接收HTTP请求并将请求分发给相应的控制器进行处理。在Spring MVC中,通常会配置一个全局的DispatcherServlet实例,以确保整个应用程序中只有一个DispatcherServlet实例。
    • 等等…

还有一些其他使用场景,例如 DefaultBeanNameGenerator、SimpleAutowireCandidateResolver 等等。

image-20240310165437362

image-20240310165537876

实践指南

在实践中,我们应该注意以下几点:

  • 线程安全性:在多线程环境下,要确保单例模式的实现是线程安全的,可以考虑使用双重检查锁或者静态内部类的方式来实现单例。
  • 懒加载与饿加载:根据项目的实际需求,选择合适的单例实现方式,是懒加载还是饿加载。
  • 防止反射和序列化攻击:考虑通过私有构造函数、枚举类型等方式来防止反射和序列化攻击。

本文总结

好了,总结一下:

本文详细介绍了单例模式在设计模式中的重要性以及在Spring框架中的应用场景。首先,我们从单例模式的基本概念出发,介绍了其特点和优势,包括全局唯一实例、延迟加载、线程安全等。然后,我们深入讨论了单例模式的五种经典实现案例,包括饿汉式、懒汉式、双重检验锁、静态内部类和枚举方式,每种实现方式的优缺点都进行了详细分析和比较。接着,我们探讨了单例模式在Spring框架中的应用场景,涵盖了Bean管理、IoC容器、AOP代理等各个方面。最后,我们给出了一些实践指南,帮助读者在实际项目中正确使用单例模式。

综上所述,单例模式作为一种简单但又非常重要的设计模式,在实际开发中有着广泛的应用,特别是在Spring框架等大型应用程序中。通过深入理解和正确应用单例模式,可以有效提高系统的性能和可维护性,是每个Java开发者都应该掌握的重要知识点。

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

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

相关文章

OpenCV 绘制文字的介绍

1、前言 OpenCV 提供了用于绘制文字的putText()方法 使用这个方法不仅能够设置字体的样式、大小和颜色&#xff0c;而且能够使字体呈现斜体的效果&#xff0c;还能够控制文字的方向&#xff0c;进而使文字呈现垂直像的效果。putText()方法的语法格式如下 需要注意的是&#x…

新版ui周易测算网站H5源码/在线起名网站源码/运势测算网站系统源码,附带系统搭建教程

支持对接第三方支付 安装方法以linux为例 1、建议在服务器上面安装宝塔面板&#xff0c;以便操作&#xff0c;高逼格技术员可以忽略这步操作。 2、把安装包文件解压到根目录&#xff0c;同时建立数据库&#xff0c;把数据文件导入数据库 3、修改核心文件config/inc_config.…

java通过poi-tl生成word

我看公司之前做电子合同&#xff0c;使用TIBCO jaspersoft做的报表模板&#xff0c;如果是给自己公司开发或者给客户做项目&#xff0c;这个也没有什么&#xff0c;因为反正模板是固定的&#xff0c;一次性开发&#xff0c;不用担心后续的问题。即使后期有调整&#xff0c;改一…

023—pandas 扩展逗号爆炸分隔字符串数据

需求&#xff1a; 将 c1 按逗号拆分&#xff0c;爆炸为一行一行数据&#xff0c;然后将 c1 后边的有逗号的扩展成行&#xff0c;没逗号的只写在第一行。 思路&#xff1a; 先将 DataFrame 中有逗号的值分拆转为列表&#xff0c;接下来我们对 c1 进行爆炸&#xff0c;就得到了…

排序算法全景:从基础到高级的Java实现

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

(黑马出品_06)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式

&#xff08;黑马出品_06&#xff09;SpringCloudRabbitMQDockerRedis搜索分布式 微服务技术ES搜索和数据分析 今日目标1. 查询文档1.1.DSL查询分类1.2.全文检索查询1.2.1.使用场景1.2.2.基本语法1.2.3.示例 1.3.精准查询1.3.1.term查询1.3.2.ran…

【机器学习】包裹式特征选择之基于模型的特征选择法

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;机器学习 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进…

解决Ubuntu 16.04/18.04 图形化界面异常、鼠标光标消失、鼠标变成叉叉等问题

bug场景&#xff1a; 一切从一次换源说起…叭叭叭 这篇文章解决的问题&#xff1a; 1.换源&#xff0c;默认源太慢&#xff0c;换成可用的阿里云的源 2.apt-get failed to …问题 3.图形化异常问题 4.get unmet dependence 问题 5. 鼠标光标消失和鼠标变成叉叉问题。 解决方…

指针篇章(3)-(指针之间的区别)

学习目录 ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————…

Windows和Linux(Ubuntu)双系统下,完全删除Ubuntu系统

彻底删除Windows和Linux&#xff08;Ubuntu&#xff09;双系统下的Ubuntu系统及其开机引导项 &#xff08;一&#xff09;删除Ubuntu系统占用的磁盘分区&#xff08;在Windows下操作&#xff09; 1.右键单击“此电脑”&#xff0c;点击“管理”&#xff1b; 2.点击左侧的“…

分布式之SpringCloud

一、SpringCloud 1、SpringCloud是什么 Spring Cloud是一系列框架的有序集合&#xff0c;这些框架为我们提供了分布式系统构建工具。 2、SpringCloud包含那些项目 项目项目名称服务注册于发现Alibaba Nacos、Netflix Eureka、Apache Zookper分布式配置中心Alibaba Nacos、S…

SpringBoot项目如何部署到服务器

文章目录 准备&#xff1a;方式一&#xff1a;Jar包方式&#xff08;推荐&#xff09;部署步骤&#xff1a; 方式二&#xff1a;War包方式部署步骤&#xff1a; 总结 准备&#xff1a; 云服务器&#xff08;阿里云、腾讯云等&#xff09;Linux系统以及运行所需的环境 方式一&a…

GIS在地质灾害危险性评估与灾后重建中的应用

地质灾害是指全球地壳自然地质演化过程中&#xff0c;由于地球内动力、外动力或者人为地质动力作用下导致的自然地质和人类的自然灾害突发事件。由于降水、地震等自然作用下&#xff0c;地质灾害在世界范围内频繁发生。我国除滑坡灾害外&#xff0c;还包括崩塌、泥石流、地面沉…

【牛客】VL68 同步FIFO

描述 请设计带有空满信号的同步FIFO&#xff0c;FIFO的深度和宽度可配置。双口RAM的参考代码和接口信号已给出&#xff0c;请在答案中添加并例化此部分代码。 电路的接口如下图所示。端口说明如下表。 接口电路图如下&#xff1a; 双口RAM端口说明&#xff1a; 端口名I/O描述…

【JAVA】CSS3伸缩盒案例、响应式布局、BFC

1.CSS3伸缩盒案例 效果&#xff1a;用伸缩盒模型 <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title>&…

深入理解Java多线程与线程池:提升程序性能的利器

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; 目录 引言 一、实现多线程 1.1. 继承Thread类 1.2. 实现Runnab…

Biomedical knowledge graph-enhanced prompt generation for large language models

1. 生物医学知识图谱增强大语言模型提示生成 论文地址&#xff1a;[2311.17330] Biomedical knowledge graph-enhanced prompt generation for large language models (arxiv.org) 源码地址&#xff1a;https://github.com/BaranziniLab/KG_RAG 2. 摘要 大语言模型&#xff0…

Linux:非常实用的Linux命令

非常实用的Linux命令 系统服务管理 systemctl systemctl命令是Systemd系统和服务管理器的一部分&#xff0c;用于控制systemd系统和服务管理器。Systemd是大多数最新的Linux发行版使用的初始化系统和服务管理器&#xff0c;它用于启动守护进程并管理它们的运行。systemctl提…

基于Java+springboot+VUE+redis实现的前后端分类版网上商城项目

基于Java springbootVUEredis实现的前后端分类版网上商城项目 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言…

C++类和对象(六):初始化列表

再谈构造函数 初始化列表 问题描述&#xff1a;虽然之前调用构造函数后&#xff0c;对象中的成员变量已经有了初始值&#xff0c;但是这仍然不能称之为对对象中成员变量的初始化&#xff0c;只能叫做赋初值&#xff0c;因为成员变量只能被初始化一次&#xff0c;而之前的构造…