01、创建型-单例模式--只有一个实例

在这里插入图片描述

文章目录

  • 前言
  • 一、基本介绍
    • 1.1 什么是单例模式
    • 1.2 为什么要用单例模式
    • 1.3 应用场景
    • 1.4 单例优缺点
  • 二、单例模式的实现方式
    • 2.1 饿汉式单例
      • 2.1.1 静态变量方式
      • 2.1.2 静态代码块
    • 2.2 懒汉式单例
      • 2.2.1 懒汉式单例
      • 2.2.2 懒汉式优化①-线程安全
      • 2.2.2 懒汉式优化②-双重检查锁
      • 2.2.3 懒汉式优化③-静态内部类
  • 三、小结

前言

  单例模式是设计模式中最简单但又最常用的的设计模式之一,是很多人学的第一个设计模式。引用百度百科的定义:单例模式创建的类在当前进程中,保证一个类只会被实例化一次,并提供了全局访问点,使用的时候通过单例提供的方法来获取实例。在确保线程安全的前提下,很多时候我们只需要同一个类的一个实例即可,而不是在任何使用的地方都实例化一个新对象,因此只能生成一个实例的模式就是单例模式。

一、基本介绍

1.1 什么是单例模式

  单例模式(Singleton Pattern) 是 Java 设计模式中最简单的设计模式之一,是指在内存中 只会且仅 创建一次对象的设计模式,无论什么时候都要保证这一点。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
  单例模式是指一个类只有一个实例,且该类能自行创建这个实例的一种模式,使用的时候通过单例提供的一个静态方法来获取实例。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。单例模式在现实生活中的应用也非常广泛,例如公司 CEO、部门经理等都属于单例模型,单例模式就是为了实现全局一个实例的需求,如下图所示。
在这里插入图片描述

注意:

  • 单例类保证内存里只有一个实例,减少了内存的开销。
  • 单例类必须自己创建自己的唯一实例
  • 单例类必须给所有其他对象提供这一实例

1.2 为什么要用单例模式

  在程序中多次使用同一个对象且作用相同时,对象需要频繁的创建和销毁的时候,而创建和销毁的过程由jvm执行,我们无法对其进行优化,为了防止内存飙升、减少了内存开支,单例模式可以让程序仅在内存中创建一个对象,并提供一个访问它的全局访问点,让所有需要调用的地方都共享这一单例对象。单例模式包含的角色只有一个,就是单例类——Singleton。

Heap
singleton
class1
class2
class3
.....
method1
method2
method3

  单例模式可以避免对资源的多重占用,避免出现多线程的复杂问题。单例类拥有一个私有构造函数,确保用户无法通过new关键字直接实例化它。除此之外,该模式中包含一个静态私有成员变量与静态公有的工厂方法,该工厂方法负责检验实例的存在性并实例化自己,然后存储在静态成员变量中,以确保只有一个实例被创建。

1.3 应用场景

  • 在应用场景中,某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。
  • 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。
  • 当某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
  • 应用程序的日志应用,一般都是单例模式实现,只有一个实例去操作才好,否则内容不好追加显示。
  • 多线程的线程池的设计一般也是采用单例模式,因为线程池要方便对池中的线程进行控制。

1.4 单例优缺点

  • 优点:
    1. 在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就防止其它对象对自己的实例化,确保所有的对象都访问一个实例。
    2. 单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
    3. 由于在系统内存中只存在一个对象,因此可以节约系统资源,当需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能,避免对共享资源的多重占用。
  • 缺点:
    1. 不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
    2. 由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
    3. 单例类的职责过重,在一定程度上违背了"单一职责原则"。
    4. 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。

二、单例模式的实现方式

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

分类说明
饿汉式类加载的时候就会创建实例对象。
懒汉式类加载不会创建实例对象,而是首次使用该对象时才会创建。

2.1 饿汉式单例

2.1.1 静态变量方式

  饿汉式单例模式在类初实话的时候就会进行实例化,简单理解类似于一个"饥饿"的人,在程序启动时就要马上吃饭(创建实例),不管后续是否会真正需要使用这个实例。
  该模式的特点是通过静态修饰符修饰,类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了,在程序调用时直接返回该单例对象即可。这种方式比较常用,调用效率高,在类加载时就创建实例对象,因此不存在 线程安全 的问题。但不管程序用不用,实例都早以创建好,这对内存来说是种浪费,容易产生垃圾对象。

程序启动
类加载
创建单例对象
适用单例对象
返回单例对象
public class HungrySingleton {// 创建私有变量 ourInstance,用以记录 Singleton 的唯一实例。private static HungrySingleton HungryInstance = new HungrySingleton();// 把类的构造方法私有化,不让外部调用构造方法实例化private HungrySingleton() {}// 定义公有方法提供该类的全局唯一访问点,外部通过调用getInstance()方法来返回唯一的实例。public static HungrySingleton getInstance() {return HungryInstance;}
}

  饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。"饿汉式"比起“懒汉式”最大的优点就是没有加锁,执行效率会提高很多,但由于是在类加载时就初始化,容易产生垃圾对象,可能会导致资源浪费,尤其是在实例对象较大或者初始化过程较为复杂的情况下。

2.1.2 静态代码块

这种方式和上面没有基本没有什么区别,都会浪费内存。

public class HungrySingleton {private static HungrySingleton instance;private HungrySingleton() {}static {instance = new HungrySingleton();}public static HungrySingleton getInstance() {return instance;}
}

2.2 懒汉式单例

2.2.1 懒汉式单例

  懒汉式创建对象的方法是在真正使用对象前,先判断该对象是否已经实例化(判空),若已实例化直接返回该类对象,否则先执行实例化操作。只有在需要时才创建实例对象,节省了系统资源,具备懒加载功能。如下图所示:

YES
NO
适用单例对象
是否实例化
返回单例对象
实例化对象

  该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例。我们创建一个 LazySingleton 类,LazySingleton 类将其构造函数作为私有,并具有自身的静态实例,使用 LazySingleton 类提供静态方法来获取 LazySingleton 对象。

public class LazySingleton {// 使用静态变量保存唯一实例private static LazySingleton instance;// 将构造方法设为私有,防止外部实例化private Singleton() {}// 提供全局访问点,获取唯一实例public static LazySingleton getInstance() {return instance == null ? new LazySingleton() : instance;}
}

  这种方式是最基本的实现方式,这种实现最大的好处就是第一次调用才初始化,如果没有用到该类,那么就不会实例化,从而节约资源,避免内存浪费。但缺点也比较明显,在多线程环境下是不安全的,如果多个线程能够同时进入,并且此时 instance 为 null,那么会有多个线程执行 instance == null,这将导致多次实例化 instance。

2.2.2 懒汉式优化①-线程安全

  不就是多线程嘛,加锁!由此,你得出了第一种优化方案,给 getInstance() 方法加锁:

public class LazySingleton {private static LazySingleton instance;private LazySingleton() {System.out.println("构造参数初始化");}// 保证每次只有一个线程进入getInstance()方法public static synchronized LazySingleton getInstance() {return instance == null ? new LazySingleton() : instance;}
}

  因为考虑到了多线程机制,实现起来比较麻烦,并且还会出现问题,就算是使用了一定的解救办法(同步、加锁、双重判断)的办法,保证在一个时间点只能有一个线程能够进入该方法,从而避免多次实例化 instance 的问题。但是当一个线程进入该方法之后,其它试图进入该方法的线程都必须等待,性能还是被损耗了,因此懒汉式方法的不推荐使用。

2.2.2 懒汉式优化②-双重检查锁

  很显然,加锁之后,达到了我们的目的,既实现了懒加载特效,也解决了线程安全问题。但是加锁之后,会导致该方法的执行效率特别低,其实就是初始化的时候才会出现线程安全,一旦初始化完成就不存在了。因此,需要把锁的粒度降低。不在方法上加锁,在关键问题上上锁。

public class Singleton {private Singleton() {}/*** 和饿汉模式相比,这边不需要先实例化出来,注意这里的 volatile,它是必须的*/private static volatile Singleton instance;public static Singleton getInstance() {if (instance == null) {// 加锁synchronized (Singleton.class) {// 这一次判断也是必须的,不然会有并发问题if (instance == null) {instance = new Singleton();}}}return instance;}
}

  通过双重检验锁机制来确保只有一个实例对象被创建,解决了懒汉式单例模式的性能、线程安全等问题,看起来是完美无缺的,其实是存在问题的,在多线程下,可能会出现空指针问题。

2.2.3 懒汉式优化③-静态内部类

  静态内部类单例模式由内部类创建,结合了懒汉式和饿汉式各自的优点,利用类加载机制保证了静态内部类只会被加载一次,并初始化其静态属性,从而保证了单例的线程安全性。而只有在需要时才会加载静态内部类,从而实现了延迟加载。

public class Singleton {// 私有的静态内部类private static class SingletonHolder {// 私有的静态变量private static final Singleton singletonFour = new Singleton();}private Singleton (){System.out.println("构造参数初始化");}public static final Singleton getInstance() {return SingletonHolder.singletonFour;}
}

  代码中增加了内部静态类 SingletonHolder,内部有一个singletonFour 的实例,并且也是类级别的。当 Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 getInstance() 方法从而触发 SingletonHolder.singletonFour 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例。这种方式不仅具有延迟初始化的好处,而且由虚拟机提供了对线程安全的支持。看起来像是饿汉式,其实这是懒汉式。因为内部静态类是现在第一次使用的时候才会去初始化,所以SingletonHolder最初并未被初始化。

三、小结

  综上所述,单例模式作为一种简单但又非常重要的设计模式,在实际开发中有着广泛的应用,特别是在Spring框架等大型应用程序中。单例模式虽然简单,但是想写的严谨,还是需要考虑周全。
  当一个类的对象只需要或者只可能有一个时,应该考虑单例模式。如果一个类的实例应该在 JVM 初始化时被创建出来,应该考虑使用饿汉式。如果一个类的实例不需要预先被创建,也许这个类的实例并不一定能用得上,也许这个类的实例创建过程比较耗费时间,也许就是真的没必要提前创建,那么应该考虑懒汉式。非线程安全的懒汉式只能用于非并发的场景,局限性比较大,并不推荐使用。

把今天最好的表现当作明天最新的起点…….~

在这里插入图片描述

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

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

相关文章

图书租赁系统-扣费服务

resources中添加moment.js文件。 然后引入moment.js文件&#xff1a; <script src"/js/moment.js"></script>借阅结束时间选完后changeDate事件&#xff1a; $("input[nameendTime]").datetimepicker({format: "yyyy-mm-dd hh:ii",…

分享基于鸿蒙OpenHarmony的Unity团结引擎应用开发赛

该赛题旨在鼓励更多开发者基于OpenHarmony4.x版本&#xff0c;使用团结引擎创造出精彩的游戏与应用。本次大赛分为“创新游戏”与“创新3D 化应用”两大赛道&#xff0c;每赛道又分“大众组”与“高校组”&#xff0c;让不同背景的开发者同台竞技。无论你是游戏开发者&#xff…

【NoC片上网络 On-Chip Network】应用程序的网络流量 合成网络流量

应用程序的网络流量 and 合成网络流量 1. 应用程序的网络流量 APPLICATION TRAFFIC2. 合成网络流量 SYNTHETIC TRAFFIC3. 合成网络流量的具体介绍 应用程序的网络流量 and 合成网络流量 1. 应用程序的网络流量 APPLICATION TRAFFIC 在 MPSoC(多处理器片上系统) 中&#xff…

网络安全之CSRFSSRF漏洞(上篇)(技术进阶)

目录 一&#xff0c;CSRF篇 二&#xff0c;认识什么是CSRF 三&#xff0c;实现CSRF攻击的前提 四&#xff0c;实战演练 【1】案例1 【2】案例2 【3】案例3 【4】案例4&#xff08;metinfo&#xff09; 一&#xff0c;CSRF篇 二&#xff0c;认识什么是CSRF CSRF&#x…

使用ollama部署llama3-8B

windows系统 安装ollama教程如下&#xff1a;https://juejin.cn/post/7359821944147722280 如果你不仅仅满足于本地自己调试&#xff0c;还希望同事也能够访问 那么按照下面步骤走&#xff08;windows系统&#xff09; set OLLAMA_HOST0.0.0.0 ollama serve然后同一个局域网下…

uniapp app权限说明弹框2024.4.23更新

华为上架被拒绝 用uni-app开发的app&#xff0c;上架华为被拒&#xff0c;问题如下&#xff1a; 您的应用在运行时&#xff0c;未见向用户告知权限申请的目的&#xff0c;向用户索取&#xff08;电话、相机、存储&#xff09;等权限&#xff0c;不符合华为应用市场审核标准。…

图解《图搜索算法》及代码实现

关注我&#xff0c;持续分享逻辑思维&管理思维&#xff1b; 可提供大厂面试辅导、及定制化求职/在职/管理/架构辅导&#xff1b; 有意找工作的同学&#xff0c;请参考博主的原创&#xff1a;《面试官心得--面试前应该如何准备》&#xff0c;《面试官心得--面试时如何进行自…

晶圆制造之MPW(多项目晶圆)简介

01、MPW是什么&#xff1f; 在半导体行业中&#xff0c;MPW 是 "Multi Project Wafer" 的缩写&#xff0c;中文意思是多项目晶圆。MPW 的主要思想是将使用相同工艺的多个集成电路设计放在同一晶圆片上进行流片&#xff08;即制造&#xff09;。这种方法允许多个设计共…

维基百科、百度百科和搜狗百科词条的创建流程

随着网络的发展&#xff0c;百度百科、搜狗百科、维基百科等百科网站已经成为大众获取知识的重要途径。因为百科具有得天独厚的平台优势&#xff0c;百科上的信息可信度高&#xff0c;权威性强。所以百科平台也成为商家的必争之地。这里小马识途聊聊如何创建百度百科、搜狗百科…

机器学习模型效果不好及其解决办法

当训练出来的机器学习模型效果不佳时&#xff0c;可能涉及多个方面的原因。为了改善模型的效果&#xff0c;需要系统地检查和分析问题的根源&#xff0c;并采取相应的措施进行优化。 一、数据问题 数据质量 检查数据是否干净、完整&#xff0c;是否存在噪声、异常值或缺失值。…

BBS前后端混合项目--01

总路由 # urls.py """BBS1 URL ConfigurationThe urlpatterns list routes URLs to views. For more information please see:https://docs.djangoproject.com/en/3.2/topics/http/urls/ Examples: Function views1. Add an import: from my_app import views2…

python绘图时渐变的处理——以一个扇形图的渐变为例

python绘图时渐变的处理——以一个扇形图的渐变为例 使用matplotlib绘制扇形的圆环 from matplotlib.patches import Wedge wedgeWedge((0,0),1,0,60,width0.3,colorred) wedge.set_edgecolor(k) fig,axplt.subplots(1,1) ax.add_patch(wedge) # 设置坐标轴的比例 plt.axis(e…

学习Rust第14天:HashMaps

今天我们来看看Rust中的hashmaps&#xff0c;在 std::collections crate中可用&#xff0c;是存储键值对的有效数据结构。本文介绍了创建、插入、访问、更新和迭代散列表等基本操作。通过一个计算单词出现次数的实际例子&#xff0c;我们展示了它们在现实世界中的实用性。Hashm…

C++ map和set的应用

1. 关联式容器 我们已经接触过STL中的部分容器&#xff0c;比如&#xff1a;vector、list、deque、 forward_list(C11)等&#xff0c;这些容器统称为序列式容器&#xff0c;因为其底层为线性序列的数据结构&#xff0c;里面存储的是元素本身。那什么是关联式容器&#xff1f;它…

开源模型应用落地-chatglm3-6b-集成langchain(十)

一、前言 langchain框架调用本地模型&#xff0c;使得用户可以直接提出问题或发送指令&#xff0c;而无需担心具体的步骤或流程。通过LangChain和chatglm3-6b模型的整合&#xff0c;可以更好地处理对话&#xff0c;提供更智能、更准确的响应&#xff0c;从而提高对话系统的性能…

【NOI】C++算法设计入门之深度优先搜索

文章目录 前言一、深度优先搜索1.引入2.概念3.迷宫问题中的DFS算法步骤4.特点5.时间、空间复杂度5.1 时间复杂度 (Time Complexity)5.2 空间复杂度 (Space Complexity)5.3 小结 二、例题讲解1.问题&#xff1a;1586 - 扫地机器人问题&#xff1a;1430 - 迷宫出口 三、总结四、感…

appium相关的知识

>adb shell dumpsys window | findstr mCurrentFocus adb devices # 实例化字典 desired_caps = dict() desired_caps[platformName] = Android desired_caps[platformVersion] = 9 # devices desired_caps[deviceName] = emulator-5554 # 包名 desired_caps[appPackage] …

IDEA pom.xml依赖警告

IDEA中&#xff0c;有时 pom.xml 中会出现如下提示&#xff1a; IDEA 2022.1 升级了检测易受攻击的 Maven 和 Gradle 依赖项&#xff0c;并建议修正&#xff0c;通过插件 Package Checker 捆绑到 IDE 中。 这并不是引用错误&#xff0c;不用担心。如果实在强迫症不想看到这个提…

STM32点灯大师(中断法)

一、使用CubeMX配置 新增加了RCC进行配置 二、代码 需要重写虚函数&#xff0c;给自己引用

JavaSE——常用API进阶二(8/8)-Arrays、Comparable、Comparator(Arrays类提供的的常见方法、用法示例)

目录 Arrays Arrays类提供的的常见方法 用法示例 Comparable、Comparator Comparable Comparator 本篇学习Arrays&#xff0c;不算作是重点知识&#xff0c;但是为学习后面的Lambda表达式打一个基础&#xff0c;或者说&#xff0c;作为铺垫。 Arrays 用来操作数组的一个…