一、单例模式
文章目录
- 1 基本介绍
- 2 实现方式
- 2.1 饿汉式
- 2.1.1 代码
- 2.1.2 特性
- 2.2 懒汉式 ( 线程不安全 )
- 2.2.1 代码
- 2.2.2 特性
- 2.3 懒汉式 ( 线程安全 )
- 2.3.1 代码
- 2.3.2 特性
- 2.4 双重检查
- 2.4.1 代码
- 2.4.2 特性
- 2.5 静态内部类
- 2.5.1 代码
- 2.5.2 特性
- 2.6 枚举
- 2.6.1 代码
- 2.6.2 特性
- 3 实现的要点
- 4 线程不安全的单例模式
- 4.1 代码
- 4.2 评价
- 5 JDK中的单例模式
- 6 单例模式的类图及角色
- 6.1 类图
- 6.2 角色
- 7 推荐的单例模式的实现
- 8 单例模式的使用场景
1 基本介绍
单例模式(Singleton Pattern)是一种 创建型 设计模式,其目的是 确保一个类仅有一个实例,并提供一个 静态方法 来获取该实例。
2 实现方式
单例模式围绕着两个特性展开:
- 延迟加载:在需要这个单例时才创建单例,避免浪费内存。
- 线程安全:在多线程环境下,保证多线程使用的单例是同一个单例。
共有以下六种实现方式:
2.1 饿汉式
2.1.1 代码
饿汉式的实现在 Java 中有两种实现,常用的是第一种。
方式一:显式初始化。
public class Singleton {private static final Singleton INSTANCE = new Singleton();private Singleton() {}public static synchronized Singleton getInstance() {return INSTANCE;}
}
方式二:静态代码块初始化。
public class Singleton {private static final Singleton INSTANCE;static {INSTANCE = new Singleton();}private Singleton() {}public static synchronized Singleton getInstance() {return INSTANCE;}
}
2.1.2 特性
- 不延迟加载:在 类加载 时就创建单例。
- 线程安全:类加载由 JVM 保证线程安全,所以此时创建的单例也是线程安全的。
2.2 懒汉式 ( 线程不安全 )
2.2.1 代码
public class Singleton {private static Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
2.2.2 特性
- 延迟加载:只有在 调用获取单例的方法
getInstance()时才创建单例。 - 线程不安全:如果有多个线程同时通过了
singleton == null这个条件,则它们会创建多个单例。
2.3 懒汉式 ( 线程安全 )
2.3.1 代码
注意 getInstance() 方法前的 synchronized 修饰符。
public class Singleton {private static Singleton instance;private Singleton() {}public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
2.3.2 特性
- 延迟加载:只有在 调用获取单例的方法
getInstance()时才创建单例。 - 线程安全:在多个线程同时获取单例时,使用
synchronized互斥锁,来保证只有一个线程能够生成单例,而其他线程等待这个线程创建的单例,保证了单例的线程安全。 - 成本太大:即使已经有单例了,每次调用
getInstance()方法还得经过 加锁 和 释放锁 的流程(因为使用了synchronized互斥锁),降低了并发性能。
2.4 双重检查
2.4.1 代码
注意单例前的 volatile 修饰符,它有两个作用:保证变量对所有线程可见 和 防止指令重排。在此处起 防止指令重排 的作用:防止 JIT 即时编译器对 instance = new Singleton(); 这行代码进行重排序。
如果进行重排序,则可能先给 instance 分配内存(此时 instance != null),然后才调用构造器为 instance 的属性赋值。在这两步操作之间,要是有线程调用 getInstance() 方法,它将无法通过外层的 instance == null 条件,会返回一个不完整(赋值不完全)的对象。
public class Singleton {private static volatile Singleton instance; // 注意 volatile 在这里起 防止指令重排 的作用private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}}
2.4.2 特性
- 延迟加载:只有在 调用获取单例的方法
getInstance()时才创建单例。 - 线程安全:在多个线程同时获取单例 且 单例未创建时,如果都通过了外层的
instance == null条件,则在内层使用synchronized互斥锁,来保证只有一个线程创建单例,而其他线程等待这个线程创建的单例,保证了单例的线程安全。 - 成本小:这种实现方式只有最初创建单例时会加互斥锁,之后就不需要创建单例了,直接返回即可,无需加锁和释放锁,提高了并发性能。
2.5 静态内部类
2.5.1 代码
public class Singleton {private Singleton() {}private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}
2.5.2 特性
- 延迟加载:只有在 调用获取单例的方法
getInstance()时,才会 触发静态内部类的加载,从而创建单例。 - 线程安全:静态内部类也是类,类加载由 JVM 保证其线程安全,所以本单例是线程安全的。
2.6 枚举
2.6.1 代码
public enum Singleton {INSTANCE; // 直接使用 Singleton.INSTANCE 就可以获取到单例// 可以随意写方法和属性,就像在类中一样
}
2.6.2 特性
- 不延迟加载:在 类加载 时就创建单例。
- 线程安全:类加载由 JVM 保证线程安全,所以此时创建的单例也是线程安全的。
- 防止 反射 或 反序列化 破坏单例:其他单例的实现都可以通过 反射 或 反序列化 的方式重新创建新的单例,唯独本实现无法使用这两种方式重新创建新的单例,这是因为 枚举无法通过反射获取对象,并且 枚举在序列化和反序列化时不会调用构造器。所以这种实现是 最推荐的。
3 实现的要点
- 构造器私有化。例如
private Singleton() {}。 - 类中有一个 静态 的单例属性。
- 提供一个 静态 方法来获取上述单例。
4 线程不安全的单例模式
4.1 代码
public class Singleton {private static Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {instance = new Singleton();}}return instance;}
}
4.2 评价
这样的单例模式是 线程不安全 的,synchronized 互斥锁没有起到应有的作用。只要多个线程都能通过 instance == null 条件,则它们每个线程都会创建一次单例,synchronized 仅仅能保证同一时刻只有一个线程在创建单例罢了。
应该将 判断 和 赋值 都放到 synchronized 互斥锁里,就像单例的 第三种实现——懒汉式 ( 线程安全 ) 一样。
5 JDK中的单例模式
在JDK中,Runtime 类使用了 饿汉式单例,代码如下:
public class Runtime {private static final Runtime currentRuntime = new Runtime();public static Runtime getRuntime() {return currentRuntime;}private Runtime() {}// ...
}
6 单例模式的类图及角色
6.1 类图

其中,singleton 属性和 Singleton() 构造器都是 私有的,而 getInstance() 方法是 公开的。此外,singleton 属性和 getInstance() 方法都是 静态的。
6.2 角色
在单例模式中,只有一个角色 Singleton,它负责 实现返回单例的 静态 方法。
7 推荐的单例模式的实现
- 饿汉式
- 双重检查
- 静态内部类
- 枚举
8 单例模式的使用场景
- 创建对象耗时过多或耗费资源过多(重量级),但经常用到。
- 频繁访问 数据库 或 文件 的对象。
- 工具类对象。
相关文章:
一、单例模式
文章目录 1 基本介绍2 实现方式2.1 饿汉式2.1.1 代码2.1.2 特性 2.2 懒汉式 ( 线程不安全 )2.2.1 代码2.2.2 特性 2.3 懒汉式 ( 线程安全 )2.3.1 代码2.3.2 特性 2.4 双重检查2.4.1 代码2.4.2 特性 2.5 静态内部类2.5.1 代码2.5.2 特性 2.6 枚举2.6.1 代码2.6.2 特性 3 实现的要…...
B树:高效的数据存储结构
在计算机科学中,B树(B-Tree)是一种平衡多路查找树,它广泛应用于数据库和文件系统等需要高效数据存储和检索的场景。B树的设计旨在优化磁盘I/O操作,通过减少磁盘访问次数来提高数据检索的效率。本文将介绍B树的基本概念…...
[Vulnhub] TORMENT IRC+FTP+CUPS+SMTP+apache配置文件权限提升+pkexec权限提升
信息收集 IP AddressOpening Ports192.168.101.152TCP:21,22,25,80,111,139,143,445,631 $ nmap -p- 192.168.101.152 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 2.0.8 or later | ftp-anon: Anonymous FTP login a…...
<数据集>安全帽佩戴识别数据集<目标检测>
数据集格式:VOCYOLO格式 图片数量:3912张 图片分辨率:640640 标注数量(xml文件个数):3912 标注数量(txt文件个数):3912 标注类别数:2 标注类别名称:[no-helmet, helmet] 序号类别名称图片…...
[米联客-安路飞龙DR1-FPSOC] FPGA基础篇连载-21 VTC视频时序控制器设计
软件版本:Anlogic -TD5.9.1-DR1_ES1.1 操作系统:WIN10 64bit 硬件平台:适用安路(Anlogic)FPGA 实验平台:米联客-MLK-L1-CZ06-DR1M90G开发板 板卡获取平台:https://milianke.tmall.com/ 登录“米联客”FPGA社区 ht…...
记录uni-app横屏项目:自定义弹出框
目录 前言: 正文: 前言:横屏的尺寸问题 最近使用了uniapp写了一个横屏的微信小程序和H5的项目,也是本人首次写的横屏项目,多少是有点踩坑不太适应。。。 先说最让我一脸懵的点,尺寸大小,下面一…...
Linux Vim教程(二):基本命令和操作
目录 1. 进入和退出Vim 1.1 启动Vim 1.2 退出Vim 2. 模式切换 2.1 切换到插入模式 2.2 切换到普通模式 2.3 切换到命令模式 2.4 切换到可视模式 3. 移动光标 4. 编辑文本 4.1 插入和追加文本 4.2 删除文本 4.3 复制和粘贴文本 4.4 撤销和重做 5. 搜索和替换 5.…...
【大模型基础】4.1 数据挖掘(待)
一、什么是文本挖掘? 文本挖掘指的是从文本数据中获取有价值的信息和知识,它是数据挖掘中的一种方法。文本挖掘中最重要最基本的应用是实现文本的分类和聚类,前者是有监督的挖掘算法,后者是无监督的挖掘算法。 二、文本挖掘的作用是什么? 能够从文本数据中获取有价值的…...
Jupyter Notebook与机器学习:使用Scikit-Learn构建模型
Jupyter Notebook与机器学习:使用Scikit-Learn构建模型 介绍 Jupyter Notebook是一款强大的交互式开发环境,广泛应用于数据科学和机器学习领域。Scikit-Learn是一个流行的Python机器学习库,提供了简单高效的工具用于数据挖掘和数据分析。本…...
IMU提升相机清晰度
近期,一项来自北京理工大学和北京师范大学的团队公布了一项创新性的研究成果,他们将惯性测量单元(IMU)和图像处理算法相结合,显著提升了非均匀相机抖动下图像去模糊的准确性。 研究团队利用IMU捕捉相机的运动数据&…...
掌握SQL Server性能监控:自定义性能计数器的实现
掌握SQL Server性能监控:自定义性能计数器的实现 在数据库管理中,监控数据库性能是确保系统稳定运行的关键。SQL Server提供了丰富的性能监控工具,但有时这些工具可能无法满足特定的监控需求。这时,自定义性能计数器就显得尤为重…...
jdk1.8 List集合Stream流式处理
jdk1.8 List集合Stream流式处理 一、介绍(为什么需要流Stream,能解决什么问题?)1.1 什么是 Stream?1.2 常见的创建Stream方法1.3 常见的中间操作1.4 常见的终端操作 二、创建流Stream2.1 Collection的.stream()方法2.2 数组创建流2.3 静态工厂…...
leetcode位运算(1720. 解码异或后的数组)
前言 经过前期的基础训练以及部分实战练习,粗略掌握了各种题型的解题思路。后续开始专项练习。 描述 未知 整数数组 arr 由 n 个非负整数组成。 经编码后变为长度为 n - 1 的另一个整数数组 encoded ,其中 encoded[i] arr[i] XOR arr[i 1] 。例如&am…...
Android 性能优化之卡顿优化
文章目录 Android 性能优化之卡顿优化卡顿检测TraceView配置缺点 StricktMode配置违规代码 BlockCanary配置问题代码缺点 ANRANR原因ANRWatchDog监测解决方案 Android 性能优化之卡顿优化 卡顿检测 TraceViewStricktModelBlockCanary TraceView 配置 Debug.startMethodTra…...
mac电脑显示隐藏文件
方法一: 第一步:打开「终端」应用程序。 第二步:输入如下命令: defaults write com.apple.finder AppleShowAllFiles -boolean true ; killall Finder 第三步:按下「Return」键确认。 现在你将会在 Finder 窗口中…...
深度学习之基础知识整理
现在大语言模型很火,但它的基础仍然是以神经网络为基础的深度学习,不懂神经网络,不了解深度学习,对于大语言模型的二次开发也是整不明白。 那到底需要了解哪些知识?才能看懂深度学习/神经网络的基础模型,想…...
R语言学习笔记9-数据过滤-分组-融合
R语言学习笔记9-数据过滤-分组-融合 数据过滤基础数据过滤条件筛选数据使用dplyr包进行数据操作select 函数filter 函数subset函数 数据分组使用split()进行数据分组使用dplyr包进行数据分组使用data.table包进行数据分组 数据融合使用merge()进行数据融合使用dplyr包进行数据融…...
GESP CCF C++ 八级认证真题 2024年6月
第 1 题 GESP活动期间,举办方从获胜者ABCDE五个人中选出三个人排成一队升国旗,其中A不能排在队首,请问 有多少种排法? A.24 B.48 C.32 D.12 第 2 题 7进制数235转换成3进制数是( )。 A. 11121 B. 11…...
Spring Boot 单元测试什么时候需要添加 @RunWith
建立 Spring Boot 单元测试方法一般依赖于 JUnit4 或 JUnit5 框架。 在高版本的 Spring Boot 中,一般默认用的是 JUnit5。此时通过添加 SpringBootTest 注解,即可成功注入相关的 bean 对象,并进行测试。 import org.junit.jupiter.api.Test…...
鸿蒙OpenHarmony Native API【HiLog】
HiLog Overview Description: HiLog模块实现日志打印功能。 开发者可以通过使用这些接口实现日志相关功能,输出日志时可以指定日志类型、所属业务领域、日志TAG标识、日志级别等。 syscap SystemCapability.HiviewDFX.HiLog Since: 8 Summary Files File …...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
SQL Server 触发器调用存储过程实现发送 HTTP 请求
文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...
高考志愿填报管理系统---开发介绍
高考志愿填报管理系统是一款专为教育机构、学校和教师设计的学生信息管理和志愿填报辅助平台。系统基于Django框架开发,采用现代化的Web技术,为教育工作者提供高效、安全、便捷的学生管理解决方案。 ## 📋 系统概述 ### 🎯 系统定…...
图解JavaScript原型:原型链及其分析 | JavaScript图解
忽略该图的细节(如内存地址值没有用二进制) 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么:保存在堆中一块区域,同时在栈中有一块区域保存其在堆中的地址(也就是我们通常说的该变量指向谁&…...
