Java设计模式之单例模式

news2025/1/23 17:51:22

这一篇,我们来介绍下设计模式最简单的一个模式,单例模式。

二、释义以及实战

  • 2.1 单例模式的定义

单例模式,英文:Singleton Pattern,英文解释:Ensure a class has only instance,and provide a global point of access to it.翻译过来就是说,要确保一个类只有一个实例,而且自行实例化并且向整个系统提供这个实例。

  • 2.2 单例模式的使用场景

单例模式的使用场景要求可以用一句话表示,如果一个类有多个对象会导致系统可能出现问题就要采用单例模式,一般的场景如下:

a.创建一个对象需要耗费大量资源或者时间,如IO,数据库连接等。

b.生成唯一id情况。

c.工具类,一般就可以采用静态类可以满足。

  • 2.3 单例模式的实战

我们来设计一个单例模式,场景:中国古代,一般情况,某一段时间只能有一个皇帝,当然特殊情况除外,无论是大臣还是平民,求见的皇帝都是同一个,我们用代码实现这个场景

//皇帝接口
public interface Iemperor {//皇帝下命令public void sayCommand(String str);
}

//明朝皇帝实现类
public class MingEmperor implements Iemperor {private staticMingEmperor emperor=new MingEmperor(new Random().nextInt(10)+"");//皇帝身份idprivate String id;//防止破坏单例private MingEmperor(String id) {this.id = id;}@Overridepublic void sayCommand(String str) {System.out.println(str+"----------我是皇帝,这是我的id="+id);}public static MingEmperor getEmperor(){return emperor;}
}

//场景客户类
public class Client {public static void main(String[] args) {for (int i = 0; i <10 ; i++) {MingEmperor.getEmperor().sayCommand("求见皇帝");}}
} 

执行下场景类,输出结果为

这说明,我们的单例模式成功了,这里我们通过声明一个全局静态变量,在类的初始化阶段就实例化一个对象,然后每次获取都是同一个对象。这种方式被称之为:饿汉氏单例模式。该单例模式的缺点就是要在初始化时候实例化对象,如果这种模式对象太多,就会创建大量的对象,而且有些可能还用不到。所以我们就改造下这种模式,变为懒汉氏单例模式,只有在真正需要用到对象的时候才开始实例化对象。我们改造下皇帝实现类代码:

public class MingEmperor implements Iemperor {private staticMingEmperor emperor;//皇帝身份idprivate String id;//防止破坏单例private MingEmperor(String id) {this.id = id;}@Overridepublic void sayCommand(String str) {System.out.println(str+"----------我是皇帝,这是我的id="+id);}public static MingEmperor getEmperor(){if (emperor==null){emperor= new MingEmperor(new Random().nextInt(10)+"");}return emperor;}
} 

这里获取皇帝对象的时候,判断是否为空,如果为空就new一个对象,否则直接返回之前实例化过的对象。我们按照原来的场景类运行下,结果如下:

这和我们预期结果一样,难道这样就ok了吗?既然是单例的,那么多线程环境下肯定也是单例的,我们换成多线程试试。

 public static void main(String[] args) {for (int i = 0; i <10 ; i++) {new Thread(new Runnable() {@Overridepublic void run() {MingEmperor.getEmperor().sayCommand("求见皇帝");}}).start();}} 

执行结果:

出问题了,多线程环境下皇帝都不是同一个了,这在古代是要出大问题啊。那么为什么会出现这样的情况呢?因为在多线程环境下,可以理解为是并行去获取皇帝对象,那么第一个线程获取的时候,发现皇帝对象为空,那么就去new一个对象,第二个线程也有可能获取为空,那么自己也去new一个皇帝对象,所以就会出现上图这样的情况。那么我们如何改造来保证线程安全呢?有人说给获取实例的方法加上synchronized锁,没错,这样是可以解决问题,但是效率太低了,那么有没有什么更高效的方法呢?答案是,有,我们改造下代码:

public class MingEmperor implements Iemperor {//增加volatile修饰,防止虚拟机指令重排序private static volatile MingEmperor emperor;//皇帝身份idprivate String id;//防止破坏单例private MingEmperor(String id) {this.id = id;}@Overridepublic void sayCommand(String str) {System.out.println(str+"----------我是皇帝,这是我的id="+id);}public static synchronized MingEmperor getEmperor(){if (emperor==null){//采用同步代码块,缩小锁定范围,比直接同步方法效率要略高synchronized (MingEmperor.class){//这里再判断为空,是防止别的线程已经完成了实例化,这里重复实例化了,就违反了单例。if (emperor==null) emperor= new MingEmperor(new Random().nextInt(10)+"");}}return emperor;}
} 

以上代码改造,主增加了volatile修饰全局变量,该变量主要功能就是增加线程之间的可见性,同时防止指令重排序(关于volatile变量,后续我会出一片文章详细说)。另外缩小了synchronized的范围,采用同步代码块。这样就完成了线程安全的懒汉式单例模式,该写法被称为,双重检查锁定(DCL)。

其实我们结合上一篇的知识,再看看这部分代码,发现这个单例模式违反了一个原则,就是单一职责原则,按照单一职责原则,皇帝类不用关心什么单例不单例,我只要传达命令就好了。所以这个模式也告诉了大家,原则要灵活使用。

  • 2.4 单例模式的缺点

1.刚刚上面提到的,单例模式违反了单一职责。

2.单例模式严格意义上说是没有接口的,要扩展只能修改,虽然上面的例子实现了接口,但是并不能针对接口做一个单例模式,因为单例模式要求“自行实例化”,接口和抽象类是不能被实例化的。所以在每个实现类进行单例模式,就算实现了接口,每个实现类都要自己实现一套单例的逻辑,也就是造成了代码重复。

  • 2.5 单例模式的优点

单例模式主要优点就算减少了系统资源消耗,优化了系统性能。

三、总结

其实,我们用到的池化技术可以理解为单例模式的一种扩展,池化技术就是可以允许创建指定数量的实例,而单例模式就相当于池化数量为1 。所以,模式在于要消化理解,然后灵活变通使用。另外需要注意的是,我们在设计单例模式的时候还需要考虑到一种破坏单例模式的情况,就是克隆方式,虽然我们私有化了构造方法,但是克隆对象并不需要执行构造方法,所以这里也是一个潜在破坏单例模式的方式。解决方法就是单例类不要实现Cloneable接口。

四、参考

《设计模式之禅》

六、推荐阅读

《JAVA设计模式之开篇》

《带你走进java集合之ArrayList》

《带你走进java集合之HashMap》

《Java锁之ReentrantLock(一)》

《Java锁之ReentrantLock(二)》

《Java锁之ReentrantReadWriteLock》

《JAVA NIO编程入门(一)》

《JAVA NIO 编程入门(二)》

《JAVA NIO 编程入门(三)》

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

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

相关文章

黑马2022新版SSM框架教程(SpringMVC_day02)

SpringMVC_day02 文章目录SpringMVC_day021&#xff0c;SSM整合1.1 流程分析1.2 整合配置步骤1&#xff1a;创建Maven的web项目步骤2:添加依赖步骤3:创建项目包结构步骤4:创建SpringConfig配置类步骤5:创建JdbcConfig配置类步骤6:创建MybatisConfig配置类步骤7:创建jdbc.proper…

Vue(十二)

1. TodoList案例自定义事件 //App.vue <template><div id"root"><div class"todo-container"><div class"todo-wrap"><!-- addTodo添加自定义事件 --><MyHeader addTodo"addTodo"/><MyList …

Spring AOP详解

1.什么是 Spring AOP&#xff1f; AOP&#xff08;Aspect Oriented Programming&#xff09;&#xff1a;⾯向切⾯编程&#xff0c;它是⼀种思想&#xff0c;它是对某⼀类事情的 集中处理。⽐如⽤户登录权限的效验&#xff0c;没学 AOP 之前&#xff0c;我们所有需要判断⽤户登…

YACC移进规约冲突案例分析(二)output中状态机转移步骤详解

案例 calc.y %union {int ival;const char *sval; } %token <ival> NUM %nterm <ival> exp %token <sval> STR %nterm <sval> useless %left - %left * %% exp:exp exp | exp - exp | exp * exp | exp / exp | NUM ; useless: STR; %%编译 $ biso…

恭喜龙蜥获得中国开源云联盟2022年度中国“最佳开源实践案例”和“杰出开源贡献者”奖项

近日&#xff0c;由工信部中国电子技术标准化研究院主办的 2022 木兰峰会在北京圆满举办&#xff0c;峰会上正式公布了中国开源云联盟(China Open Source Cloud League&#xff0c;简称“COSCL”) 2022 年度评选名单&#xff0c;龙蜥社区荣获中国“最佳开源实践案例”和“杰出开…

仪器设备使用

NI DcpowerSwitchDigitalDMMFgenScope名称直流电源&#xff08;SMU&#xff09;继电器PPMU数字万用表信号发生器示波器版本PXI-4147PXI-2567PXI-6571PXI-4070PXI-4463PXI-5160 1.Scope 示波器是一种电子测量仪器&#xff0c;可以在无干扰的情况下监控输入信号&#xff0c;随后…

Go结构体(struct)

文章目录Struct定义struct构造struct实例struct的值和指针在与函数共用时&#xff1a;匿名字段和嵌套struct嵌套struct的名称冲突问题Struct 是一个值类型的 定义struct type identifier struct {field1 type1field2 type2… } // 或者 type T struct { a, b int }理论上&am…

JAVA多线程初阶(1)

目录JAVA多线程(1)1.Thread类创建与使用1.1 继承Thread类1.2 实现并发关于sleep()1.3 Runnable创建线程1.4 匿名内部类创建线程1.5 lamda表达式创建线程2.多线程提高效率3.Thread类属性和方法3.1 Thread(String name)3.2 isDaemon()3.3 isAlive()3.3 线程的重要方法3.4 中断线程…

数据结构:图

文章目录图内存中存储图数据结构邻接矩阵存储方法用邻接矩阵&#xff08;Adjacency Matrix&#xff09;来表示一个图的缺点&#xff1a;浪费空间优点邻接表存储方法&#xff08;Adjacency List&#xff09;广度优先算法Breadth-First-Search&#xff08;BFS&#xff09;深度优先…

Android——GT库-日志工具

GT库在创造出来初期&#xff0c;里面的日志工具就一直存在的&#xff0c;经历了很久的迭代变更&#xff0c;当目前的最新版本&#xff0c;日志工具已经创造出更高级的调试日志方式了&#xff0c;接下来咋们来看看GT库中的日志工具具体使用方法吧。 使用GT库里的&#xff0c;当然…

web表单设计器的优点体现在哪?

在数字化管理越来越规范的当下&#xff0c;拥有一款优质高效的低代码开发平台&#xff0c;确实能给企业提质增效带来更大的帮助。很多客户朋友会问道&#xff1a;web表单设计器都有哪些特点&#xff1f;为什么能在企业的现代化办公管理中起到巨大的作用&#xff1f;今天&#x…

Linux终端远程工具xshell,xftp,mobasterm

目录 软件介绍 1.xshell 第一步&#xff1a; 第二步&#xff1a; 第三步&#xff1a; 第四步&#xff1a; 第5步&#xff1a; 2.xftp 第一步&#xff1a; 第二部&#xff1a; 第三步&#xff1a; 3.mobasterm 全能终端神器——MobaXterm 第一步&#xff1a; 第二步&a…

C1083无法打开包括文件: “atlbase.h”: No such file or directory

在打开别人的项目的过程中遇到了“atlbase.h”无法打开的问题&#xff0c;在此记录一下。1.下载ATL生成工具与缓解只下载ATL生成工具后面还会报错&#xff0c;直接下载下载ATL生成工具与缓解一步到位。下载的入口在&#xff1a;工具--->获取工具与功能。需要注意的是&#x…

Guitar Pro2023Win/Mac中文吉他/贝斯打谱识谱软件

Guitar Pro 是一款曲谱阅读器。以 GTP 结尾的曲谱文件都必须用 Guitar Pro 才能打开。Guitar Pro 凭借着其便利的制谱和读曲谱环境&#xff0c;在各大谱库论坛里都占据着一席之地&#xff0c;喜欢吉他的朋友一定略有耳闻。早几年该作者将它移植到了移动平台&#xff0c;现在你也…

7-2国王游戏

题目&#xff1a; 恰逢 H 国国庆&#xff0c;国王邀请 n 位大臣来玩一个有奖游戏。 首先&#xff0c;他让每个大臣在左、右手上面分别写下一个整数&#xff0c;国王自己也在左、右手上各写一个整数。 然后&#xff0c;让这 n 位大臣排成一排&#xff0c;国王站在队伍的最前面。…

应用层——Web和HTTP

目录 1. HTTP概况 1.1 Web页面简介 1.2 URL-统一资源定位器 1.3 HTTP协议 2. HTTP连接的两种类型 2.1 HTTP非持久性连接(Non-persistent HTTP) 2.2 HTTP持久性连接(Persistent HTTP) 2.2.1 无流水(pipelining)的持久性连接 2.2.2 带有流水机制的持久性连接 3. HT…

一站式开发平台赋能办公全场景

近几年&#xff0c;数字化办公迎来了新的机遇&#xff0c;根据亿欧智库《2022中国数字化办公市场研究报告》推算&#xff0c;数字化办公2021年的市场规模达到973.89亿元&#xff0c;至2025年将达到1768.16亿元&#xff0c;整体增速保持平稳&#xff0c;2018-2025年的CAGR为15.8…

Mybatis 框架搭建封装JDBC,实现sql语句

目录 1、maven新建一个工程​编辑 2、添加POM.XML配置文件 3、创建实例包 4、创建一个环境资源根目录 5、配置环境文件 6、创建接口&#xff0c;添加方法 7、编写sql语句 8、创建测试类 8.1 、定义工厂模式 8.2 、定义会话 8.3、定义对象 8.5、获取Builder建造工厂 …

LAB3 EIGRP1实验

1 实验拓扑&#xff1a; 2 实验要求&#xff1a; 1>.R1-R3环回口0:192.168.100.x/32。 2>.R1上采用手动汇总的命令&#xff0c;汇总4条环回口成一条。 3>.R1上下发一条默认路由。 4>.实现R1到R2的环回口路由非等价负载。 5>.as 90都使用eigrp认证。 6>…

css动画效果之transition

transition-property规定设置过渡效果的 CSS 属性的名称。属性名属性值none没有属性会获得过渡效果。all所有属性都将获得过渡效果。property定义应用过渡效果的 CSS 属性名称列表&#xff0c;列表以逗号分隔。使用方式transition-property: width,background;/* 多个效果可用逗…