域名申请哪个网站好/电商平台营销策划方案
在软件开发领域,设计模式是解决常见设计问题的有效方案,而单例模式作为创建型设计模式中的一员,其重要性不容小觑。它能够确保一个类仅有一个实例,并提供全局访问点,这一特性在资源管理、配置信息读取、线程池管理以及日志记录等多个方面都发挥着关键作用。本文将深入探讨 Java 单例模式的多种实现方式、线程安全性、懒汉式与饿汉式的区别以及其应用场景等内容,助力读者全面且深入地理解并熟练运用这一设计模式。
一、单例模式的概念
单例模式的核心是对一个类的实例化次数加以限制,确保在整个应用程序运行期间,该类仅有一个实例存在。这个实例的创建时机有两种常见情况:一种是在类加载时就完成创建(饿汉式);另一种是在首次被访问时才进行创建(懒汉式)。并且,会提供一个公共的静态方法作为获取该实例的唯一途径。通过这种方式,单例模式能够有效地管控资源的使用,避免因重复创建实例而导致的资源浪费,同时也为全局资源的统一管理和访问提供了极大的便利。
二、单例模式的实现方式
(一)饿汉式单例
饿汉式单例在类加载阶段就创建实例。其优势在于线程安全性由 JVM 保障,因为类加载过程本身就是线程安全的。以下是一个典型的饿汉式单例实现示例:
public class EagerSingleton {// 私有静态实例,在类加载时就初始化private static final EagerSingleton instance = new EagerSingleton();// 私有构造函数,防止外部实例化private EagerSingleton() {}// 公共静态方法获取单例实例public static EagerSingleton getInstance() {return instance;}
}
在上述代码中,EagerSingleton
类的构造函数被私有化,从而有效阻止了外部类对其进行实例化操作。instance
变量在类加载时便被创建并完成初始化,由于其被 private static final
修饰,这就确保了在整个应用程序的生命周期内,该实例的唯一性。getInstance
方法则作为全局访问点,任何需要使用这个单例实例的地方,都可以通过调用此方法获取。
(二)懒汉式单例
懒汉式单例的特点是在首次被访问时才创建实例,这种方式在一定程度上能够节省资源,但需要特别关注线程安全问题。以下是一个简单的懒汉式单例示例:
public class LazySingleton {// 私有静态实例,初始化为 nullprivate static LazySingleton instance;// 私有构造函数private LazySingleton() {}// 公共静态方法获取单例实例,需同步以保证线程安全public static synchronized LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}
}
在这个示例中,instance
变量初始被赋值为 null
。在 getInstance
方法中,首先会检查 instance
是否为 null
,若为 null
,则创建一个新的 LazySingleton
实例并赋值给 instance
。这里通过使用 synchronized
关键字来确保线程安全,其作用是在多线程环境下,当一个线程进入 getInstance
方法并创建实例时,其他线程会被阻塞在同步块之外,直至第一个线程完成实例创建并释放锁。然而,这种方式在高并发场景下,性能可能会受到较大影响,因为每次获取实例都需要进行同步检查。
(三)双重检查锁定(DCL)单例
为了优化懒汉式单例在多线程环境下的性能表现,可以采用双重检查锁定机制。这种方式在保障线程安全的同时,能够有效减少不必要的同步开销。
public class DoubleCheckedLockingSingleton {// 私有静态实例,使用 volatile 关键字保证可见性和禁止指令重排private static volatile DoubleCheckedLockingSingleton instance;// 私有构造函数private DoubleCheckedLockingSingleton() {}// 公共静态方法获取单例实例public static DoubleCheckedLockingSingleton getInstance() {if (instance == null) {synchronized (DoubleCheckedLockingSingleton.class) {if (instance == null) {instance = new DoubleCheckedLockingSingleton();}}}return instance;}
}
在上述代码中,instance
变量使用 volatile
关键字进行修饰。这是由于在多线程环境下,指令重排可能会导致其他线程获取到尚未完全初始化的 instance
。volatile
关键字能够确保变量的可见性,即一个线程对 instance
的修改能够立即被其他线程察觉,同时禁止指令重排,从而保证对象的初始化顺序正确无误。双重检查锁定机制首先进行一次非同步的检查,如果实例已经存在,那么直接返回,避免了不必要的同步操作;若实例不存在,则进入同步块再次检查并创建实例,以此确保线程安全。
(四)静态内部类单例
静态内部类单例是一种较为优雅的实现方式,它巧妙地融合了饿汉式和懒汉式的优点。
public class StaticInnerClassSingleton {// 私有构造函数private StaticInnerClassSingleton() {}// 静态内部类,在类加载时不会立即加载private static class SingletonHolder {// 静态实例,在静态内部类加载时创建private static final StaticInnerClassSingleton instance = new StaticInnerClassSingleton();}// 公共静态方法获取单例实例public static StaticInnerClassSingleton getInstance() {return SingletonHolder.instance;}
}
在这个实现中,SingletonHolder
是一个静态内部类。instance
实例在 SingletonHolder
类加载时创建,由于静态内部类只有在被使用时才会加载,所以实现了懒加载的效果。同时,类加载过程的线程安全性确保了实例的唯一性,无需额外的同步机制,既保障了线程安全又提升了性能。
(五)枚举单例
使用枚举来实现单例模式是一种简洁且线程安全的绝佳方式。在 Java 中,枚举类型的实例天然具有单例特性,并且由 JVM 保证其唯一性和线程安全性。
public enum EnumSingleton {INSTANCE;// 可以在这里定义单例的其他方法和属性public void doSomething() {System.out.println("Doing something in EnumSingleton.");}
}
在上述代码中,EnumSingleton
是一个枚举类型,仅有一个实例 INSTANCE
。可以在枚举中定义其他方法和属性,通过 EnumSingleton.INSTANCE
即可访问这个单例实例并调用其方法。
三、线程安全性分析
(一)饿汉式
饿汉式单例在类加载时就创建实例,由于类加载过程由 JVM 保证是线程安全的,所以在多线程环境下,无论多个线程同时访问 getInstance
方法多少次,获取到的都将是同一个预先创建好的实例,不会出现多个实例的情况。
(二)懒汉式
如前文所述,简单的懒汉式单例通过在 getInstance
方法上使用 synchronized
关键字来确保线程安全。在多线程环境中,当一个线程进入 getInstance
方法并创建实例时,其他线程会被阻塞在同步块之外,直至第一个线程完成实例创建并释放锁。这种方式虽然能够保证线程安全,但同步开销较大,尤其是在高并发场景下,会对性能产生明显的影响。
(三)双重检查锁定(DCL)
双重检查锁定机制借助 volatile
关键字和两次 if
检查来保障线程安全。第一次非同步检查能够减少不必要的同步开销,第二次同步块内的检查则确保了在多线程竞争的情况下,只有一个线程能够成功创建实例。volatile
关键字保证了变量的可见性和禁止指令重排,有效避免了其他线程获取到未完全初始化的实例。
(四)静态内部类
静态内部类单例利用了类加载的线程安全性。SingletonHolder
类只有在 getInstance
方法被调用时才会加载,而类加载过程是线程安全的,所以在多线程环境下不会出现多个实例的情况。
(五)枚举
枚举单例由 JVM 保证其线程安全性,在多线程环境下,无论多少个线程访问 EnumSingleton.INSTANCE
,获取到的都将是同一个实例,并且不会出现实例化多次的问题。
四、懒汉式与饿汉式的区别
(一)创建时机
- 饿汉式:在类加载时就创建实例,无论该实例是否在后续的程序运行中被实际使用,都会提前占用内存资源。这种方式适用于实例创建过程相对简单、占用资源较少,并且在应用程序启动后就需要立即使用单例实例的场景。例如,一些基础的配置类,其在应用启动时就需要被加载并使用。
- 懒汉式:在首次被访问时才创建实例,延迟了实例的创建过程,只有在真正需要使用该实例时才会占用内存资源。对于那些创建过程复杂、资源消耗大或者不一定会被使用到的单例对象,懒汉式单例能够显著提高资源利用率。比如,某些涉及到复杂数据库连接或网络初始化的单例对象,如果采用饿汉式可能会在应用启动时就进行不必要的资源消耗,而懒汉式则可以避免这种情况。
(二)线程安全性
- 饿汉式:由于类加载过程的线程安全性,饿汉式单例天生就是线程安全的,无需额外的同步机制。这使得在多线程环境下使用饿汉式单例时,无需担心线程安全问题,代码实现相对简单。
- 懒汉式:简单的懒汉式单例需要借助同步机制(如
synchronized
关键字)来保证线程安全,这必然会带来一定的性能开销。尽管可以通过双重检查锁定等优化方式来减少同步开销,但代码相对复杂,并且需要考虑volatile
关键字等因素,增加了代码的维护难度。
(三)性能表现
- 饿汉式:在类加载时创建实例可能会导致应用程序的启动时间略微延长,尤其是当单例对象的创建过程较为复杂时。然而,在应用程序运行过程中,由于不需要进行同步检查,获取实例的速度较快,能够提供较好的运行时性能。
- 懒汉式:在低并发场景下,懒汉式单例的性能表现可能较好,因为只有在需要时才创建实例,避免了不必要的资源占用。但在高并发场景下,如果同步机制处理不当,会导致性能大幅下降,因为多个线程可能会竞争锁资源,造成线程阻塞和等待,从而影响整体性能。
五、单例模式的应用场景
(一)资源共享与管理
例如数据库连接池,在应用程序中通常只需要一个数据库连接池实例来统一管理数据库连接资源。多个数据库操作可以共享这个连接池,通过单例模式能够方便地实现连接池的全局访问和资源管理,有效避免创建多个连接池导致的资源浪费和性能下降。如果每个数据库操作都创建自己的连接池,不仅会消耗大量的系统资源,还会增加数据库连接的管理复杂性,而单例模式的数据库连接池可以很好地解决这些问题。
(二)配置信息读取
应用程序的配置信息在整个运行期间通常是固定不变的,如数据库配置、日志配置等。可以采用单例模式创建一个配置管理器,负责读取和管理配置信息,其他模块则可以通过单例实例获取配置信息,这样能够确保配置信息的一致性和全局可用性。例如,在一个大型的企业级应用中,不同的模块可能都需要访问数据库配置信息,如果没有单例模式的配置管理器,每个模块都自行读取配置文件,可能会导致配置不一致的问题,并且增加了配置文件管理的难度。
(三)线程池管理
线程池在多线程编程中用于管理线程资源,提高线程的复用性和性能。使用单例模式创建线程池,可以在整个应用程序中共享同一个线程池实例,方便对线程任务进行统一的调度和管理,避免创建多个线程池带来的资源竞争和管理复杂性。例如,在一个 Web 应用中,多个请求处理可能都需要使用线程池来执行异步任务,如果每个请求都创建自己的线程池,会导致系统资源的过度消耗和线程管理的混乱,而单例模式的线程池可以有效地解决这些问题。
(四)日志记录器
在一个应用程序中,通常只需要一个日志记录器来记录各种日志信息。单例模式可以确保只有一个日志记录器实例存在,方便对日志的输出格式、级别、目标等进行统一管理和配置,并且在多线程环境下也能保证日志记录的顺序和完整性。例如,在一个分布式系统中,多个节点可能都会产生日志信息,如果每个节点都有自己的日志记录器,那么在日志收集和分析时会面临诸多困难,而单例模式的日志记录器可以将所有节点的日志信息统一管理,便于后续的处理和分析。
六、总结
Java 单例模式是一种极为实用的设计模式,通过限制类的实例化次数为一次,并提供全局访问点,在资源管理、配置信息处理、线程池和日志记录等众多场景中都有着广泛的应用。本文详细介绍了饿汉式、懒汉式、双重检查锁定、静态内部类和枚举等多种单例模式的实现方式,深入分析了它们的线程安全性、懒汉式与饿汉式的区别以及应用场景。在实际开发过程中,开发人员需要依据具体的需求和场景,仔细权衡资源占用、线程安全和性能等多方面因素,从而选择最为合适的单例模式实现方式,以此构建高效、可靠的 Java 应用程序。同时,随着 Java 语言的不断发展以及编程规范的持续演进,对于单例模式的理解和应用也需要不断深入和优化,以更好地适应日益复杂的软件开发需求。
希望通过本文的详细介绍,读者能够对 Java 单例模式有更为透彻的理解,并能够在实际项目开发中灵活自如地运用这一设计模式,从而有效提升软件设计和开发的质量与效率。
相关文章:

Java 单例模式:深度解析与应用
在软件开发领域,设计模式是解决常见设计问题的有效方案,而单例模式作为创建型设计模式中的一员,其重要性不容小觑。它能够确保一个类仅有一个实例,并提供全局访问点,这一特性在资源管理、配置信息读取、线程池管理以及…...

软件质量保证——单元测试之白盒技术
笔记内容及图片整理自XJTUSE “软件质量保证” 课程ppt,仅供学习交流使用,谢谢。 程序图 程序图定义 程序图P(V,E),V是节点的集合(节点是程序中的语句或语句片段),E是有向边的集合…...

Vue0-生命周期-03
生命周期 生命周期指定就是一个对象从创建到销毁的整个过程。 Vue也是有的 完整的Vue周期包含8个阶段。 Vue官方生命周期流程图: 那这有什么用呢?我们可以在指定阶段做特殊的事件。 这些方法伴随生命周期的进行自动执行。 <!DOCTYPE html> <…...

Flutter:页面滚动
1、单一页面,没有列表没分页的,推荐使用:SingleChildScrollView() return Scaffold(backgroundColor: Color(0xffF6F6F6),body: SingleChildScrollView(child: _buildView()) );2、列表没分页,如购物车页,每个item之间…...

【CameraPoseRefinement】以BARF为例介绍三维重建中的位姿优化
文章目录 IntroductionApproachPlanar Image Alignment(2D)Neural Radiance Fields (3D)Bundle-Adjusting Neural Radiance Fields Experiment平面图像对齐的定性实验合成场景上的定量实验 Introduction 在计算机视觉三维重建中,求解3D场景的表示和定位给定的相机帧…...

YOLO系列论文综述(从YOLOv1到YOLOv11)【第13篇:YOLOv10——实时端到端物体检测】
YOLOv10 1 摘要2 网络结构3 YOLOv1-v10对比 YOLO系列博文: 【第1篇:概述物体检测算法发展史、YOLO应用领域、评价指标和NMS】【第2篇:YOLO系列论文、代码和主要优缺点汇总】【第3篇:YOLOv1——YOLO的开山之作】【第4篇:…...

多数元素
多数元素 给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 示例 1: 输入:nums [3,2,3] 输出ÿ…...

EasyDSS视频推拉流技术的应用与安防摄像机视频采集参数
安防摄像机的视频采集参数对于确保监控系统的有效性和图像质量至关重要。这些参数不仅影响视频的清晰度和流畅度,还直接影响存储和网络传输的需求。 安防摄像机图像效果的好坏,由DSP处理器和图像传感器sensor决定,如何利用好已有的硬件资源&…...

在CentOS7上更换为阿里云源
在CentOS 7上更换为阿里云YUM源可以通过以下步骤进行: 备份当前的YUM源配置文件 sudo mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 下载阿里云的YUM源配置文件 sudo curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirr…...

小程序跳转到本页面并传参
const pages getCurrentPages(); const currentPage pages[pages.length - 1]; // 当前页面路由 const route currentPage.route; // 当前页面参数 const options currentPage.options;// 构建新的 URL 参数 const newOptions {...options,// newParam: newValue }; // 你…...

Vim操作
1. Vim的模式 2.正常模式->编辑模式 在上⽅插⼊⼀⾏: O在下⽅插⼊⼀⾏: o (open)在当前光标前插⼊: i在⾏⾸插⼊: I在当前光标后插⼊: a在⾏尾插⼊: A 3.常见命令行 1、拷贝当前行 yy ,拷贝当前行向下…...

金碟云星空-企微通知
需求背景: 通过企业微信,及时发送金碟云星空消息,比如流程异常、审批节点、等需要关注数据和信息点 需求目的: 及时告警、高响应、自动化 技能要求: 前后端开发工具的运用与开发,本实例使用IDEA 企业…...

Java中的运算符“instanceof“详解
在Java中,instanceof运算符用于检查一个对象是否是某个特定类的实例,或者是否实现了某个特定接口。它返回一个布尔值(true或false),用于在运行时进行类型检查。这在处理多态性时尤其有用,可以帮助我们确定对…...

SVG无功补偿装置MATLAB仿真模型
“电气仔推送”获得资料(专享优惠) 模型简介 SVG(又称ASVG 或STATCOM)是Static Var Generator 的缩写,叫做静止无功发生器。也是做无功补偿的,比SVC 更加先进。其基本原理是将自换相桥式电路通过电抗器或…...

Java 虚拟机:承载 Java 生态的神奇魔盒
在软件开发的世界里,Java 虚拟机(JVM)就像一位智慧的管家,默默守护着 Java 生态系统的运行。它不仅让 Java 实现了"一次编写,到处运行"的梦想,更是成为了多种编程语言的运行平台。让我们一起走进…...

多输入多输出 | Matlab实现TCN-LSTM时间卷积神经网络结合长短期记忆神经网络多输入多输出预测
多输入多输出 | Matlab实现TCN-LSTM时间卷积神经网络结合长短期记忆神经网络多输入多输出预测 目录 多输入多输出 | Matlab实现TCN-LSTM时间卷积神经网络结合长短期记忆神经网络多输入多输出预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 多输入多输出 | Matlab实现…...

快速排序算法讲解(c基础)
一、快速排序的基本原理 快速排序是一种基于分治策略的高效排序算法。它的基本思想是: 选择一个基准元素(pivot),通过一趟排序将待排序序列分割成两部分,其中一部分的所有元素都比基准元素小,另一部分的所有…...

数据结构--二叉树的创建和遍历
目录 引入 定义 性质 二叉树的创建 迭代法 注意事项: 递归法 注意事项: 二叉树的遍历 深度优先 广度优先 先序遍历(前序遍历) 中序遍历 后序遍历 层序遍历 查找树结构中是否存在某数值 方法一: 方法…...

2024143读书笔记|《遇见》——立在城市的飞尘里,我们是一列忧愁而又快乐的树
2024143读书笔记|《遇见》——立在城市的飞尘里,我们是一列忧愁而又快乐的树 第1章 年年岁岁岁岁年年第2章 遇见第3章 有个叫“时间”的家伙走过第4章 初雪第6章 回首风烟 《华语散文温柔的一支笔:张晓风作品集(共5册)》作者张晓风…...

计算机毕业设计Python+卷积神经网络股票预测系统 股票推荐系统 股票可视化 股票数据分析 量化交易系统 股票爬虫 股票K线图 大数据毕业设计 AI
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...

leetcode hot100【LeetCode 48.旋转图像】java实现
LeetCode 48.旋转图像 题目描述 给定一个 n x n 的二维矩阵 matrix,表示一个图像。请你将该图像顺时针旋转 90 度。 说明: 你必须在 原地 修改输入的二维矩阵。你可以假设矩阵的所有元素将会是整数。 示例 1: 输入: [[1, 2, 3],[4, 5, 6],[7, 8, …...

力扣1382:将二叉搜索树便平衡
给你一棵二叉搜索树,请你返回一棵 平衡后 的二叉搜索树,新生成的树应该与原来的树有着相同的节点值。如果有多种构造方法,请你返回任意一种。 如果一棵二叉搜索树中,每个节点的两棵子树高度差不超过 1 ,我们就称这棵二…...

ElasticSearch学习篇19_《检索技术核心20讲》搜推广系统设计思想
目录 主要是包含搜推广系统的基本模块简单介绍,另有一些流程、设计思想的分析。 搜索引擎 基本模块检索流程 查询分析查询纠错 广告引擎 基于标签倒排索引召回基于向量ANN检索召回打分机制:非精确打分精准深度学习模型打分索引精简:必要的…...

实战ansible-playbook:Ansible Vault加密敏感数据(三)
在实际生产环境中,使用 Ansible Vault 来加密敏感数据是一种常见的做法。以下是一个详细的步骤和实际生产环境的使用案例,展示如何使用 Ansible Vault 来加密和管理敏感数据。 1. 安装 Ansible 确保你已经安装了 Ansible。如果还没有安装,可以使用以下命令进行安装: # 在…...

Python 视频合并工具
Python 视频合并工具 1.简介: 这是一个使用 moviepy 和 tkinter 创建的简单图形用户界面(GUI)应用程序,用于合并两个视频文件,并在两个视频之间添加淡入淡出过渡效果。程序的功能是: 选择两个视频&#…...

JavaScript实用工具lodash库
Lodash中文文档: Lodash 简介 | Lodash中文文档 | Lodash中文网 Lodash是一个功能强大、易于使用的JavaScript实用工具库,它提供了丰富的函数和工具,能够方便地处理集合、字符串、数值、函数等多种数据类型。通过使用Lodash,开发者可以大幅…...

mapstruct DTO转换使用
定义一个基础接口 package com.example.mapstruct;import org.mapstruct.Named;import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.Date; import java.util.List;/*** Author zmn Dat…...

Linux(Centos7)---安装nginx(很简单)
安装 sudo yum install nginx -y开机启动与开启服务 sudo systemctl enable nginx sudo systemctl start nginx...

【接口调试】OpenAI ChatGPT API
【接口调试】AbortController 发出请求finish_reason 参数细节 – Openai ChatGPT 文档 发出请求 可以将以下命令粘贴到终端中以运行第一个API请求。 请确保用您的秘密API密钥替换$OPENAI_API_KEY。 curl https://api.openai.com/v1/chat/completions \-H "Content-Ty…...

云轴科技ZStack助力 “上科大智慧校园信创云平台”入选上海市2024年优秀信创解决方案
近日,为激发创新活⼒,促进信创⾏业⾼质量发展,由上海市经济信息化委会同上海市委网信办、上海市密码管理局、上海市国资委等主办的“2024年上海市优秀信创解决方案”征集遴选活动圆满落幕。云轴科技ZStack支持的“上科大智慧校园信创云平台”…...