设计模式之单例模式(通俗易懂,超详细)

2023-10-27

1 什么是单例模式

单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例)
——《百度百科》

简单来说单例模式就是指在内存中只会创建且仅创建一次对象的设计模式,当程序中其他地方需要使用到该对象的相同功能时,都会调用创建好的这一个,不会再额外创建实例,这样做的好处就是避免过多的创建相同作用的对象使得内存浪费。

2 单例模式分类

在单例模式中主要分为两类,分别是懒汉式和饿汉式

  1. 懒汉式:在程序调用时才创建实例
  2. 饿汉式:在程序加载时就创建好实例,等待被调用

3 单例模式的实现

3.1 懒汉式

懒汉式是在程序调用时才会创建实例,在程序调用时首先会进行判断,如果已经存在该实例,则直接返回,若不存在该实例则创建并返回,懒汉式流程图如下:
懒汉式流程图
代码实现
为避免类被多次实例化,所以将类中的构造函数限定为private,这样就能保证其他程序无法通过new关键字来实例化,达到真正的单例。

package com.lee.singleton;

public class Singleton {
	private static Singleton instence = null;
	private Singleton() {}
	public static Singleton getInstence() {
		if(instence == null)
			instence =  new Singleton();
		return instence;
	}
}

通过以上代码就简单的实现了懒汉式的单例模式,但是在上面的代码中还有不完美的地方:如果当程序为多线程时,那么如果线程A和线程B同时调用了Singleton的getInstence()方法,然后同时进入了if判断,这样的话由于线程不安全就会导致被实例化两个对象。
下面通过两个线程来模拟调用该单例模式程序,然后通过实例的hashCode来判断是只创建了一个对象还是创建了两个对象。
测试多线程调用单例模式

public class SingletonTest {
	public static void main(String[] args) {
		Thread thread = new Thread() {
			public void run() {
				Singleton instence = Singleton.getInstence();
				int hashCode = instence.hashCode();
				System.out.println(Thread.currentThread().getName() + ":" + hashCode);
			}
		};
		thread.start();
		Thread thread2 = new Thread() {
			public void run() {
				Singleton instence = Singleton.getInstence();
				int hashCode = instence.hashCode();
				System.out.println(Thread.currentThread().getName() + ":" + hashCode);
			}
		};
		thread2.start();
	}
}

运行结果如下所示:
运行结果
由结果我们可以看到两个线程所创建的对象的hashCode值不一样,那么也就代表如果多线程调用该单例模式则会出现线程安全问题。

3.2 懒汉式优化

如果说到解决线程安全问题,最先想到的肯定就是synchronized方法和synchronized代码块。最简单的就是在getInstence方法上加上synchronized,这样当一个线程进入到该方法时,另外的线程就无法进入,可以确保单例。
使用synchronized

//方法加synchronized锁的方式
public synchronized static Singleton getInstence() {
	if(instence == null)
		instence =  new Singleton();
	return instence;
}

//synchronized代码块的方式
public static Singleton getInstence() {
	synchronized(Singleton.class){
		if(instence == null)
			instence =  new Singleton();
	}
	return instence;
}

但是使用以上方法还是不够完美,那就是当已经有了实例之后每次调用getInstence还是会首先获取到锁,然后再进行判断,这样当高并发的情况下性能就会及其低下,所以如果需要安全并且性能好的话就只能使用以下方法。
最终优化方法

public Singleton getInstence() {
	if(instence == null) {
		synchronized(Singleton.class) {
			if(instence == null) {
				instence = new Singleton();	
			}	
		}
	}
	return instence;
}

该方法只是在synchronized代码块的方式外层又加了一层if判断,这样的话当已经有实例的情况下就会直接返回实例化并不会进入到if中,也就不会去获取锁。并且在没有实例的情况下如果两个线程都进入到了最外层的if判断,那么在线程A获取到锁并进入第二层if中并实例化对象之后,线程B就不会进入到第二层if,能够确保单例。

3.2 饿汉式

饿汉式是指在程序加载时就创建对象,当需要调用时则直接返回实例,不需要和懒汉式一样进行判断是否实例化,饿汉式流程图如下所示:
饿汉式流程图
代码实现

public class Singleton {
	private static final Singleton instence = new Singleton();
	private Singleton() {}
	public Singleton getInstence() {
		return instence;
	}
}

4 总结

  1. 单例模式就是在内存中只会创建且仅创建一次对象的设计模式,因为只创建一次对象,所以构造方法私有化,通过getInstence方法获取对象。
  2. 单例模式分为懒汉式和饿汉式,懒汉式是在调用时创建对象,需要注意线程安全和性能优化,饿汉式是在程序加载时就创建对象,需要时直接调用。
  3. 在开发时如果对于内存的要求特别高,使用懒汉式,在需要时才创建,如果对内存要求不高使用饿汉式,饿汉式简单不易出错,而且没有并发安全和性能问题。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

设计模式之单例模式(通俗易懂,超详细) 的相关文章

  • Run As JUnit 未出现在 Eclipse 中 - 使用 JUnit4

    我正在尝试为我的 Web 应用程序编写 JUnit4 测试 它们之前一直工作正常 但是 现在当我尝试通过右键单击类文件 gt Run As gt JUnit Test 来运行测试时 我看不到该选项 我认为这可能是因为一位同事意外提交了一些
  • 获取 Spring Boot 中当前活动数据源的引用

    我想通过实现数据库数据初始化DataSourceInitializer 我将这些方法放在我的 Spring Boot 主方法下面 但似乎它根本没有被执行 我尝试故意删除字符只是为了触发一个错误来确认执行 什么也没有发生 Configurat
  • 如何显示 javadoc 代码块中的泛型?

    我有一个 javadoc 代码块 我想在其中编写一个包含泛型的代码示例 如下所示 public interface SomeInterface
  • 将 Tango 3D 点投影到屏幕 Google Project Tango

    Project Tango 提供了点云 如何获取点云中 3D 点的像素位置 以米为单位 我尝试使用投影矩阵 但得到的值非常小 0 5 1 3 等 而不是 1234 324 以像素为单位 我包含我尝试过的代码 Get the current
  • JRuby调用了错误的方法

    我在调用 Java 方法时遇到了一个奇怪的问题JRuby http en wikipedia org wiki JRuby 在我的 Java 类中 这些方法定义了两次 看来 JRuby 调用了错误的方法 所以我尝试使用java method
  • 方法中缺少 return 语句错误

    我正在尝试编写一个返回计算机 MAC 地址字符串的静态方法 该函数本身可以在此处找到 http www mkyong com java how to get mac address in java http www mkyong com j
  • 从java类文件获取apache webcontents文件夹的绝对路径[重复]

    这个问题在这里已经有答案了 需要在动态 Web 应用程序内获取 java 类文件中的绝对路径 实际上我需要获取 apache webapps 文件夹的路径 部署 webapps 的位置 e g apache root webapps my
  • Scala 不可变 Map 速度慢

    当我创建地图时 我有一段代码 val map gtfLineArr 8 split map split collect case Array k v gt k v toMap 然后我使用这张地图来创建我的对象 case class MyOb
  • 在 Ubuntu 上的 Tomcat 中加载共享本机库

    如何在 Ubuntu 上的 Tomcat6 中加载共享库 我创建了一个名为 libawragrids so 的库 awragrids 并将其放置在 var lib tomcat6 shared 我在调用启动 tomcat 的终端中设置了以下
  • Java:当计时器处于活动状态时,JSplitPane 将顶部面板的内容复制到底部面板

    所以我有一个 JSplitPane 和两个 JPanel 一个在顶部 一个在底部 在这两个面板中 我重写了paintComponent方法并添加了我自己的图形 在底部面板中 我想添加动画 当面板不重新绘制时 这很好 但是一旦计时器 java
  • DocumentBuilder 解析产生无效字节 2 of 4 字节 UTF-8 序列错误

    我正在尝试解析包含字符串的字节数组Impresi n in XML final DocumentBuilderFactory builderFactory DocumentBuilderFactory newInstance final D
  • 从 google play 中提取统计信息

    我正在建立一些统计数据 并希望获得来自 google play 应用程序商店 的统计数据 最受欢迎 下载量 价格等信息 有谁知道是否有这个 API 或者我必须自己抓取它 有一个名为 android market api 的项目http co
  • 如何在触摸屏幕时播放声音? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我正在尝试制作一款类似飞翔的小鸟的游戏 然而 我正在努力寻找一种在触摸屏幕时播放流行声音的方法 我希望它在每次触摸屏幕时播放声音 我是
  • 如何配置 Spring boot 以使用两个数据库?

    我在用春季启动 2 X with 休眠5连接两个不同的 MySQL 数据库 Bar 和 Foo 在不同的服务器上 我试图列出一个实体的所有信息 自己的属性和 OneToMany and ManyToOne关系 来自 REST 控制器中的方法
  • 如何从特定偏移量的kafka主题消费到特定偏移量的数据?

    我需要消耗特定偏移量到特定结束偏移量 Consumer seek 从特定偏移量读取数据 但我需要检索从offset到tooffset的数据 任何帮助将不胜感激 提前致谢 ConsumerRecords
  • JVM中为什么要交换对象位置? [复制]

    这个问题在这里已经有答案了 这是我尝试过的 public final class firstObj public static void main String args Object obj new Object Object obj1
  • 如何开始编写代码覆盖率工具? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 寻找实际讨论的书籍或其他参考资料how用Java编写代码覆盖率工具 一些不同的技术或技巧 源代码与字节
  • 使用数组或列表在 JSP 中自动完成文本框

    我试图以不同的方式进行自动完成 但根本不起作用 从here https stackoverflow com questions 18612524 jquery autocomplete ui with servlet is not retu
  • Java线程创建开销

    传统观点告诉我们 大容量企业 Java 应用程序应该优先使用线程池 而不是生成新的工作线程 指某东西的用途java util concurrent使这变得简单 然而 确实存在线程池不适合的情况 我目前正在努力解决的具体例子是使用Inheri
  • 如何在Java(IDE)中找到当前项目目录的路径?

    我试图在 Java 中以编程方式找到当前正在运行 调试的项目的路径 我在 Google 中查找 发现的是System getProperty user id 这没有让我得到项目的路径 我知道命令Environment currentDire

随机推荐

  • PTA4(python)

    程序设计04 选择与循环 7 1 身体质量指数 高教社 Python编程基础及应用 习题6 3 7 2 计算分段函数f x 的值 7 3 程序员买西瓜 7 4 超速处罚 加测试数据 7 5 数据比较 7 6 运输打折问题 7 7 jmu p
  • 【Easyexcel】根据模板导出excel

    主要实现通过模板 导出对应数据 生成excel 1 版本 Maven com alibaba easyexcel 3 0 5 2 代码实现 ExportExcelBase 是定义一些非列表的字段 如下 导出时间等 工具类 import co
  • Windows系统中Apache Http服务器简单使用

    1 简介 Apache HTTP服务器是一个开源的 跨平台的Web服务器软件 它由Apache软件基金会开发和维护 Apache HTTP服务器可以在多种操作系统上运行 如Windows Linux Unix等 并且支持多种编程语言和技术
  • 基于Swagger3.0的真实项目常用注解

    文章目录 entity层 mapper层 Service层 query层 VO层 Convert层 Controller层 entity层 作用在类上 Schema 类和字段皆用 Swagger3 0提供的注解 用来描述类或字段 Data
  • 什么是决策表?

    1 决策表是一种用于表示和分析决策逻辑的表格形式 它通常用于制定规则 以便根据条件和决策动作来确定适当的行动 2 决策表的主要构成部分是规则 规则由条件和动作组成 条件是指某些情况或特定事件的发生 而动作则是在满足条件的情况下需要执行的行动
  • 给定一个二叉树的根节点 root ,返回它的 中序 遍历。【LeetCode Hot 100】

    力扣热题100之第94题 方法一 递归法 首先我们得知道什么是二叉树的中序遍历 左子树节点 根节点 右子树节点 就是遇到节点时 优先遍历该节点的左子树 等遍历完了再到根节点 最后是右子树 如下图所示 那么这棵二叉树中序遍历的结果就为 4 2
  • hive详解(函数)

    hive函数分为内置函数和自定义函数 内置函数 show functions 查看函数 desc function 查看用法 排名函数 3种 row number 没有并列 相同名次按顺序排 同分不同名 rank 有并列 相同名次空位 de
  • Blas xGEMMBatched launch failed的出现原因

    如果你的cudatoolkit是9 x版本的 在执行两个很大的batch做matmal的时候 可能会报一个很奇怪的错误 但是实际上你的显存是够的 为什么会报这样的错误呢 这个问题困扰了我好几天 从网上查阅了很多资料 才发现是cublas的内
  • SqlHelper

    import pymysql pymysql install as MySQLdb 默认连接MySQL的方式 class SqlHelper def init self account password ip port db self ac
  • 【IDEA常用快捷键】

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 一 Goland快捷键 前言 提示 快捷键可以提升熟练我们工作的效率 掌握快捷键势在必行 提示 以下是本篇文章正文内容 下面案例可供参考 一 Goland快捷
  • 网上挣钱方法有哪些?这6个方法是目前最稳的!

    君子爱财 取之有道 随着互联网的发展 网络上的赚钱项目也是越来越多 具体要选择哪一种呢 很多人都幻想着一夜暴富 但是真正一夜暴富的方法都写在了法律里面 并不是一下子就能赚到钱的 这是非常不切实际的想法 今天小编就跟大家分享几种比较赚钱的网络
  • IDEA查看项目依赖插件

    IDEA查看项目依赖插件 搜索不到 HTTP Proxy 代理 重启IDEA 在Plugin中搜索Maven Helper Install 如果再搜索不到可以去官网下载查卷 然后再导入 版本要对应IDEA的版本 安装完成以后重启IDEA 打
  • Python爬取天气数据及可视化分析

    Python爬取天气数据及可视化分析 文章目录 Python爬取天气数据及可视化分析 说在前面 1 数据获取 请求网站链接 提取有用信息 保存csv文件 2 可视化分析 当天温度变化曲线图 当天相对湿度变化曲线图 温湿度相关性分析图 空气质
  • 划分数组

    快排的parition划分 class Solution param nums The integer array you should partition param k An integer return The index after
  • ABAP 关于BAPI的EXTENSIONIN 一点解释

    其实EXTENSIONIN 相当于BAPI的一种增强 可以这样理解 要传一下BAPI本身没有的数据 比如 自己增加的 或者是关联过来的数据 BAPI没有给提供这样的字段 那么就要自己去找了 首先要找到增强的结构 比如 BAPI REQUIS
  • 升级Flutter 3.13.x 之后出现watcher-1.0.2报错

    Failed to build intl utils generate pub cache hosted pub flutter io cn watcher 1 0 2 lib src constructable file system e
  • mesa编译

    0 准备工作 sudo apt get install git autoconf libtool dpkg dev quilt debhelper 1 libdrm编译 下载 git clone http anongit freedeskt
  • 彻聊DNS

    先得聊聊什么是域名 域名是什么 我会连域名都不知道 别着急 先看看嘛 我们以www fanyi baidu com为例 域名结构划分为根域名 顶级域名 二级域名 三级域名等 做过开发的都知道 在创建项目时 一般是com xxx xxx 这就
  • 【毕业设计】大数据用户画像数据分析系统 - python

    文章目录 1 前言 2 用户画像分析概述 2 1 用户画像构建的相关技术 2 2 标签体系 2 3 标签优先级 3 实站 百货商场用户画像描述与价值分析 3 1 数据格式 3 2 数据预处理 3 3 会员年龄构成 3 4 订单占比 消费画像
  • 设计模式之单例模式(通俗易懂,超详细)

    1 什么是单例模式 单例模式 属于创建类型的一种常用的软件设计模式 通过单例模式的方法创建的类在当前进程中只有一个实例 根据需要 也有可能一个线程中属于单例 如 仅线程上下文内使用同一个实例 百度百科 简单来说单例模式就是指在内存中只会创建