Java软件设计模式-单例设计模式

目录

1.软件设计模式的概念

2.设计模式分类

2.1 创建型模式

2.2 结构型模式

2.3 行为型模式

3.单例设计模式

3.1 单例模式的结构

 3.2 单例模式的实现

 3.2.1 饿汉式-方式1(静态变量方式)

 3.2.2 懒汉式-方式1(线程不安全)

3.2.3 懒汉式-方式2(线程安全)

3.3 单例模式的优点和缺点


1.软件设计模式的概念

软件设计模式(Software Design Pattern),又称设计模式,是一套被“反复使用”、“多数人知晓的”、“代码设计经验的总结”。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。也就是说,它是解决特定问题的一系列套路,是“前辈们的代码设计经验的总结”,具有一定的普遍性,可以反复使用。

2.设计模式分类

2.1 创建型模式

用于描述“怎样创建对象”,它主要特点是“将对象的创建与使用分离”。GoF书中提供了单例、原型、工厂方法、抽象工厂、建造者五种创建型模式。

2.2 结构型模式

用于描述如何将类或对象按某种布局组成更大的结构,GoF书中提供了代理、适配器、桥接、装饰、外观、享元、组合等 7 种结构型模式。

2.3 行为型模式

用于描述类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务,以及怎样分配职责。GoF(四人组)书中提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器11 种行为型模式。

3.单例设计模式

  • 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
  • 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

3.1 单例模式的结构

单例模式的主要有以下角色:

  • 单例类。只能创建一个实例的类.
  • 访问类。使用单例类

 3.2 单例模式的实现

单例模式的设计分为俩种:

  • 饿汉式:类加载就会导致该单实例对象被创建。
  • 懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会被创建。

 3.2.1 饿汉式-方式1(静态变量方式)

/*** 饿汉式*      静态变量创建类的对象*/
public class Singleton {//私有构造方法private Singleton() {}//在成员位置创建该类的对象private static Singleton instance = new Singleton();//对外提供静态方法获取该对象public static Singleton getInstance() {return instance;}
}

说明:

该方式在成员位置声明Singleton类型的静态变量,并创建Singleton类的对象instance。instance对象是随着类的加载而创建的。如果该对象足够大的话,而一直没有使用就会造成内存的浪费。

 3.2.2 懒汉式-方式1(线程不安全)

/*** 懒汉式*  线程不安全*/
public class Singleton {//私有构造方法private Singleton() {}//在成员位置创建该类的对象private static Singleton instance;//对外提供静态方法获取该对象public static Singleton getInstance() {if(instance == null) {instance = new Singleton();}return instance;}
}

说明:

从上面代码我们可以看出该方式在成员位置声明Singleton类型的静态变量,并没有进行对象的赋值操作,那么什么时候赋值的呢?当调用getInstance()方法获取Singleton类的对象的时候才创建Singleton类的对象,这样就实现了懒加载的效果。但是,如果是多线程环境,会出现线程安全问题。

3.2.3 懒汉式-方式2(线程安全)

/*** 懒汉式*  线程安全*/
public class Singleton {//私有构造方法private Singleton() {}//在成员位置创建该类的对象private static Singleton instance;//对外提供静态方法获取该对象public static synchronized Singleton getInstance() {if(instance == null) {instance = new Singleton();}return instance;}
}

说明:

该方式也实现了懒加载效果,同时又解决了线程安全问题。但是在getInstance()方法上添加了synchronized关键字,导致该方法的执行效果特别低。从上面代码我们可以看出,其实就是在初始化instance的时候才会出现线程安全问题,一旦初始化完成就不存在了。

3.2.4 懒汉式-方式3(双重检查锁)

再来讨论一下懒汉模式中加锁的问题,对于 getInstance() 方法来说,绝大部分的操作都是读操作,读操作是线程安全的,所以我们没必让每个线程必须持有锁才能调用该方法,我们需要调整加锁的时机。由此也产生了一种新的实现模式:双重检查锁模式。

/*** 双重检查方式*/
public class Singleton { //私有构造方法private Singleton() {}private static Singleton instance;//对外提供静态方法获取该对象public static Singleton getInstance() {//第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例if(instance == null) {synchronized (Singleton.class) {//抢到锁之后再次判断是否为nullif(instance == null) {instance = new Singleton();}}}return instance;}
}

说明:

双重检查锁模式是一种非常好的单例实现模式,解决了单例、性能、线程安全问题,上面的双重检测锁模式看上去完美无缺,其实是存在问题,在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。

要解决双重检查锁模式带来空指针异常的问题,只需要使用 volatile 关键字, volatile 关键字可以保证可见性和有序性。

/*** 双重检查方式*/
public class Singleton {//私有构造方法private Singleton() {}private static volatile Singleton instance;//对外提供静态方法获取该对象public static Singleton getInstance() {//第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实际if(instance == null) {synchronized (Singleton.class) {//抢到锁之后再次判断是否为空if(instance == null) {instance = new Singleton();}}}return instance;}
}

小结:

添加 volatile 关键字之后的双重检查锁模式是一种比较好的单例实现模式,能够保证在多线程的情况下线程安全也不会有性能问题。

还有,这里的private static volatile Singleton singleton = null;中的volatile也必不可少,volatile关键字可以防止jvm指令重排优化。

在java内存模型中,volatile 关键字作用可以是保证可见性或者禁止指令重排。这里是因为 singleton = new Singleton() ,它并非是一个原子操作,事实上,在 JVM 中上述语句至少做了以下这 3 件事:

  • 第一步是给 singleton 分配内存空间;

  • 第二步开始调用 Singleton 的构造函数等,来初始化 singleton;

  • 第三步,将 singleton 对象指向分配的内存空间(执行完这步 singleton 就不是 null 了)。

这里需要留意一下 1-2-3 的顺序,因为存在指令重排序的优化,也就是说第 2 步和第 3 步的顺序是不能保证的,最终的执行顺序,可能是 1-2-3,也有可能是 1-3-2。

如果是 1-3-2,那么在第 3 步执行完以后,singleton 就不是 null 了,可是这时第 2 步并没有执行,singleton 对象未完成初始化,它的属性的值可能不是我们所预期的值。假设此时线程 2 进入 getInstance 方法,由于 singleton 已经不是 null 了,所以会通过第一重检查并直接返回,但其实这时的 singleton 并没有完成初始化,所以使用这个实例的时候会报错,详细流程如下图所示:

 这里还说一下volatile关键字的第二个作用,保证变量在多线程运行时的可见性:

public class Test01 {public static void main(String[] args) throws Exception{T t=new T();t.start();Thread.sleep(2000);System.out.println("主线程设置t线程的参数来止损失");t.setFlag(false);}
}
class T extends Thread{private volatile boolean flag=true;public void setFlag(boolean flag) {this.flag = flag;}@Overridepublic void run() {System.out.println("进入run方法");while(flag){}}
}

 在 JDK1.2 之前,Java的内存模型实现总是从主存(即共享内存)读取变量,是不需要进行特别的注意的。而在当前 的 Java 内存模型下,线程可以把变量保存本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。这就 可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数 据的不一致。 要解决这个问题,就需要把变量声明为 volatile,这就指示 JVM,这个变量是不稳定的,每次使用它都到主存中进行 读取。

3.3 单例模式的优点和缺点

优点:单例类只有一个实例,节省了内存资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能;单例模式可以在系统设置全局的访问点,优化和共享数据,例如前面说的Web应用的页面计数器就可以用单例模式实现计数值的保存。

缺点:单例模式一般没有接口,扩展的话除了修改代码基本上没有其他途径。

下一篇:软件设计模式-工厂模式-CSDN博客

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

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

相关文章

办公灯多普勒雷达模组感应开关,飞睿智能24G毫米波雷达超低功耗uA级,节能LED灯新搭档

在科技日新月异的今天,节能、环保已经成为我们生活和工作中不可或缺的一部分。作为新时代的办公人,我们不仅要追求高效的工作方式,更要关注我们所使用的设备是否足够环保、节能。今天,我们就来聊聊一个令人兴奋的创新——飞睿智能…

如何30分钟下载完368G的Android系统源码?

如何30分钟下载完368G的Android系统源码? Android系统开发的一个痛点问题就是Android系统源码庞大,小则100G,大则,三四百G。如标题所言,本文介绍通过局域网高速网速下载源码的方法。 制作源码mirror 从源码git服务器A&#xff0c…

推荐系统:从协同过滤到深度学习

目录 一、协同过滤(Collaborative Filtering, CF)1. 基于用户的协同过滤2. 基于物品的协同过滤 二、深度学习在推荐系统中的应用1. 深度学习模型的优势2. 深度学习在推荐系统中的应用实例 三、总结与展望 推荐系统是现代信息处理和传播中不可或缺的技术&…

【话题】破茧而出:打破AI“信息茧房”,捍卫信息自由与多样性

目录 AI发展下的伦理挑战,应当如何应对? 方向一:构建可靠的AI隐私保护机制 方向二:确保AI算法的公正性和透明度 方向三:管控深度伪造技术 AI发展下的伦理挑战,应当如何应对? 在人工智能&…

Tita的OKR:高端制造行业的OKR案例

高端设备制造行业的发展趋势: 产业规模持续扩大:在高技术制造业方面,航空、航天器及设备制造业、电子工业专用设备制造等保持较快增长。新能源汽车保持产销双增,新材料新产品生产也高速增长。 标志性装备不断突破:例如…

Flink Window 窗口【更新中】

Flink Window 窗口 在Flink流式计算中,最重要的转换就是窗口转换Window,在DataStream转换图中,可以发现处处都可以对DataStream进行窗口Window计算。 窗口(window)就是从 Streaming 到 Batch 的一个桥梁。窗口将无界流…

系统架构师考点--软件工程(上)

大家好。今天我来总结一下软件工程的相关考点。这部分是考试的重点。在上午场客观题、下午场案例题以及下午场论文都有可能考到,在上午场客观题中大约占12-15分左右。 一、软件工程概述 软件开发生命周期 软件定义时期:包括可行性研究和详细需求分析过…

uniapp打包成Android时,使用uni.chooseLocation在App端显示的地址列表是空白?一直转圈的解决办法

问题描述: uniapp打包后的测试版app在ios里可以显示高德地图的定位列表,但是安卓手机却不显示定位列表,一直在转圈圈,怎么回事?之前的功能在正式版都能用,真机运行也能用,为什么测试版的安卓手…

ADC性能规格--动态性能

所有真正的ADC都有额外的噪声源和失真过程,会降低性能。 动态性能规范报告了ADC动态行为中的这些缺陷,包括总谐波失真(THD)、信噪比加失真(SND)、信号噪声比(SNR)和无杂散动态范围&a…

Study--Oracle-07-ASM自动存储管理(二)

一、ASM安装准备条件 1、ASM支持存储类型 本地祼设备(本地的磁盘和分区) 网络附加存储(NAS) 存储区域网络(SAN) 2、ASM使用本地裸设备,要点: 已经被挂载到操作系统上或者已经做了分区 映射裸设备为文件名 设置正确的权限(针对grid用户和asmadmin组,权限为660) 二、OR…

【CUDA】CUDA中缓存机制对计时的影响

笔者在阅读知乎上一个关于CUDA编程的专栏时,发现作者写的很多文章中都会附带计时的模块用于评估程序的运行效率,然而笔者发现,在运行这篇文章中的代码时时,得到的结果和作者的结果有较大差异,主要体现在:使…

Python - Word转TXT文本,或TXT文本转Word

Word文档(.doc或.docx)和纯文本文件(.txt)是两种常用的文件格式。Word文档通常用于复杂的文档处理和排版,而纯文本文件则用于存储和传输纯文本信息。了解如何在这两种格式之间进行转换能提高工作效率,并便于…

Pytorch使用Dataset加载数据

1、前言: 在阅读之前,需要配置好对应pytorch版本。 对于一般学习,使用cpu版本的即可。参考教程点我 导入pytorch包,使用如下命令即可。 import torch # 注意虽然叫pytorch,但是在引用时是引用torch2、神经网络获取…

【UE5.3】笔记11

一、变量的SET&&GET 1、创建变量保存数据,如下图,找到左侧我的蓝图下的变量,新增一个,并选择类型。使用的时候直接将变量拖到蓝图中,此时会显示两个选项一个是获取一个是设置。 选择获取就是个GET蓝图&#x…

2024文件加密软件有哪些丨超好用的文件加密软件排行榜

文件加密软件在现代数字生活中扮演着至关重要的角色,尤其是在保护个人隐私、商业机密和敏感数据方面。 加密软件可以防止未经授权的访问和数据泄露。即使设备丢失或被盗,加密后的文件也不会轻易被破解,从而保护了数据的安全。 对于企业而言…

鸿蒙next 数据缓存 你不知道的事情

《鸿蒙next ArkUI专栏》系列前言: 作者:徐庆 团队:坚果派 公众号:“大前端之旅” 润开鸿生态技术专家,华为HDE,CSDN博客专家,CSDN超级个体,CSDN特邀嘉宾,InfoQ签约作者,OpenHarmony布道师,电子发烧友专家博客,51CTO博客专家,擅长HarmonyOS/OpenHarmony应用开发、熟…

计算机网络之因特网

1.因特网简介 1.1因特网的提出 1957年,苏联发射了人类第一颗人造地球卫星"Sputnik"。作为响应,美国国防部(DoD)组建了高级研究计划局(ARPA),开始将科学技术应用于军事领域 。 1961年7月,MIT的Leonard Kleinrock…

Zabbix6.0监控Freeswitch状态

一、前提环境说明 1、最终实现Freeswitch监控指标信息: 2、环境需求: (1)需要使用Zabbix6.0及以上 (2)需要使用zabbix_agent2 二、实现步骤 1、zabbix_agent2添加监控键值 cat /etc/zabbix/conf.d/fr…

「豆包Marscode体验官」我用豆包Marscode画数据大屏

认识豆包Marscode 豆包 MarsCode IDE 是一个 AI 原生的云端集成开发环境(IDE)。内置的 AI 编程助手和开箱即用的开发环境让我们可以更加专注于各种项目的开发。豆包 MarsCode 编程助手,具备以智能代码补全为代表的 AI 功能。支持了多种编程语…

【Android面试八股文】请描述一下 android 的系统架构?

Android 是一个基于 Linux 的开源软件堆栈,针对多种不同设备类型打造。下图显示了 Android 平台的主要组件。 早期的Android架构如下图所示 官方网站最新的Android平台架构图,如下所示: Linux 内核 Android 平台的基础是 Linux 内核。例如,Android 运行时 (ART) 依赖…