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

news2024/11/19 5:35:09

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

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

一、引言

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

二、基本概念

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

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

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

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开发者都应该掌握的重要知识点。

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

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

相关文章

【图论】 【割点】 【双连通分类】LCP 54. 夺回据点

本文涉及知识点 图论 割点 双连通分类 割点原理及封装好的割点类 LeetCode LCP 54. 夺回据点 魔物了占领若干据点&#xff0c;这些据点被若干条道路相连接&#xff0c;roads[i] [x, y] 表示编号 x、y 的两个据点通过一条道路连接。 现在勇者要将按照以下原则将这些据点逐一…

Docker容器Docker桌面配置镜像加速

打开Docker Desktop应用程序&#xff0c;点击设置 具体配置如下&#xff1a; {"builder": {"gc": {"defaultKeepStorage": "20GB","enabled": true}},"experimental": false,"features": {"buil…

基于java的足球联赛管理系统(程序+数据库+文档)

** &#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;** 一、研究背景…

【数据库】软件测试之MySQL数据库练习题目

有表如下&#xff1a; Student 学生表 SC 成绩表 Course 课程表 Teacher 老师表 每个学生可以学习多门课程&#xff0c;每一个课程都有得分&#xff0c;每一门课程都有老师来教&#xff0c;一个老师可以教多个学生 1、查询姓‘朱’的学生名单 select * from Student whe…

「CISP题库精讲」CISP题库习题解析精讲20道

前言 本篇主要对CISP教材第九章《计算环境安全》的一些习题进行讲解&#xff0c;包括20道题&#xff0c;这里只是部分习题&#xff0c;针对第九章可能会多写几章的内容&#xff0c;如果我发布的这些习题里面没有你想找的那道题&#xff0c;你也可以直接私信我&#xff0c;我加…

趣学前端 | Taro迁移完成之后,总结了一些踩坑经验

背景 四月份的时候&#xff0c;尝试将老的移动端项目改造成多端。因为老项目使用的React框架&#xff0c;综合考量&#xff0c;保障当前业务开发的进度同时&#xff0c;进行项目迁移&#xff0c;所以最后选择了Taro框架。迁移成本会低一些&#xff0c;上手快一些。 上个月&am…

新书速览|Vue.js 3.x+Element Plus从入门到精通(视频教学版)

详解Vue.jsElement Plus框架各组件的用法&#xff0c;实战网上商城系统和图书借阅系统开发 本书内容 《Vue.js 3.xElement Plus从入门到精通&#xff1a;视频教学版》通过对Vue.js&#xff08;简称Vue&#xff09;的示例和综合案例的介绍与演练&#xff0c;使读者快速掌握Vue.j…

【掌握版本控制:Git 入门与实践指南】配置详解|理解本地仓库结构

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;泥中に咲く—ウォルピスカーター 0:34━━━━━━️&#x1f49f;──────── 4:46 &#x1f504; ◀️ ⏸ ▶…

excel批量数据导入时用poi将数据转化成指定实体工具类

1.实现目标 excel进行批量数据导入时&#xff0c;将批量数据转化成指定的实体集合用于数据操作&#xff0c;实现思路&#xff1a;使用注解将属性与表格中的标题进行同名绑定来赋值。 2.代码实现 2.1 目录截图如下 2.2 代码实现 package poi.constants;/*** description: 用…

力扣--动态规划/回溯算法131.分割回文串

思路分析&#xff1a; 动态规划 (DP)&#xff1a; 使用动态规划数组 dp&#xff0c;其中 dp[i][j] 表示从字符串 s[i] 到 s[j] 是否为回文子串。预处理动态规划数组&#xff1a; 从字符串末尾开始&#xff0c;遍历每个字符组合&#xff0c;判断是否为回文子串&#xff0c;填充…

像SpringBoot一样使用Flask - 2.静态资源访问及模版

一、安装并导入 render_template 功能&#xff1a;渲染/加载模板&#xff0c;一般是html页面 参数&#xff1a;函数的第一个参数是模板的文件名&#xff0c;必填&#xff0c;后面的参数都是键值对&#xff0c;表示模板中变量对应的值&#xff0c;非必填 (不填界面也不会展示成变…

使用express+nginx+pm2+postman实现推送zip包自动更新前端网页

1.nginx配置将80端口代理到项目的3000端口 server {listen 80; #监听的端口server_name localhost; #监听的域名#charset koi8-r;#access_log logs/host.access.log main;location / {#root html;#index index.html index.html;proxy_pass http://127.0.0.1:3000; #转…

Android App冷启动耗时优化

Android应用启动过程 Android应用启动过程&#xff0c;主要包含app::onCreate及执行前的Application阶段及Activity::onCreate执行之后的Activity阶段&#xff0c;以及两个阶段之间的间隙handleMessage阶段和最终页面渲染上屏完成前数据加载阶段四个区间组成。 具体来看&#x…

如何提高内存和cpu使用率呢?-Linux类资源

最近公司项目上线时&#xff0c;红蓝线巡检时&#xff0c;部分服务器因配置高但使用率低而需要降级。 为了避免降级&#xff0c;如何增加内存和CPU使用率&#xff1f; 这可以通过脚本来实现。 当前服务器操作系统版本&#xff1a;-7_5。 连接服务器后&#xff0c;登录账号。 …

深入理解Debug断点调试技巧

在软件开发过程中&#xff0c;调试是一个非常重要的环节&#xff0c;而使用断点是提高调试效率的关键技巧之一。本文将深入探讨如何利用断点进行高效的调试&#xff0c;常用的快捷键以及一些常见的断点调试技巧。 一、调试技巧&#xff1a; 1. 设置断点&#xff1a;在代码中设…

你配赚米吗?韭菜就在繁荣时进场!防诈P的最简单方法,还不告诉你爸妈?——早读(逆天打工人爬取热门微信文章解读)

你听到哪里好&#xff0c;你去了就是韭菜 引言Python 代码第一篇 人民日报 1460万&#xff0c;保住了&#xff01;第二篇 人民日报 夜读 每一次努力&#xff0c;都是幸运的伏笔第三篇 人民日报 来啦 新闻早班车要闻社会政策 结尾 “故天将降大任于是人也&#xff0c;必先苦其心…

CMake 交叉编译

想知道“魔笛手”在这里能发挥什么作用吗&#xff1f;想象一下&#xff0c;把 CMake 当做法力高强的魔笛手&#xff0c;C 的项目则是故事中的那些被魔笛手拯救的孩子。 父母要抚养一个孩子并非易事&#xff0c;营养需要面面俱到&#xff0c;保证身体健康&#xff0c;关心事无巨…

【活动】探索人工智能的“迷惑瞬间”:真实体验与技术挑战

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 标题&#xff1a;探索人工智能的“迷惑瞬间”&#xff1a;真实体验与技术挑战引言…

vulhub中Weblogic 管理控制台未授权远程命令执行漏洞复现(CVE-2020-14882,CVE-2020-14883)

Weblogic是Oracle公司推出的J2EE应用服务器。在2020年10月的更新中&#xff0c;Oracle官方修复了两个长亭科技安全研究员voidfyoo 提交的安全漏洞&#xff0c;分别是CVE-2020-14882和CVE-2020-14883。 CVE-2020-14882允许未授权的用户绕过管理控制台的权限验证访问后台&#x…

英飞凌电源管理PMIC的安全应用

摘要 本篇文档主要用来介绍英飞凌电源管理芯片TLF35584的使用&#xff0c;基于电动助力转向应用来介绍。包含一些安全机制的执行。 TLF35584介绍 TLF35584是英飞凌推出的针对车辆安全应用的电源管理芯片&#xff0c;符合ASIL D安全等级要求&#xff0c;具有高效多电源输出通道&…