单例模式的多种写法

目录

正文:

1.概念

2.饿汉式

3.懒汉式 

3.1线程不安全的懒汉式

3.2线程安全的懒汉式

4.双重检查式

 总结:


正文:

1.概念

单例模式(Singleton Pattern)是软件设计中常见的设计模式之一。它确保一个类只有一个实例,并提供一个全局访问点。这种模式在全局状态下的数据管理和控制、资源优化等方面非常有用。在Java中实现单例模式有多种方式,每种方式都有其特点和适用场景。

2.饿汉式

其特点是在类加载时就立即初始化实例,并且只创建一个实例。实例在类加载时就已经创建,所以在任何线程访问getInstance()方法之前,实例已经存在,因此不存在线程同步问题。

public class EagerSingleton {// 私有静态实例,类加载时就初始化private static final EagerSingleton instance = new EagerSingleton();// 私有构造函数,防止外部通过new创建实例private EagerSingleton() {}// 公有的静态方法,提供全局访问点public static EagerSingleton getInstance() {return instance;}
}

优点:

  1. 实现简单:代码简洁,易于理解和维护。
  2. 无需同步:由于实例在类加载时就已经创建,不需要考虑线程同步问题。

缺点:

  1. 资源占用:不管是否需要,实例都会被创建,可能导致不必要的资源占用。
  2. 不易扩展:如果需要改变单例的创建逻辑,或者需要按需加载,饿汉式可能不是最佳选择。

适用场景:

  • 单例对象的创建过程不需要消耗大量资源。
  • 单例对象需要频繁访问,且访问速度很重要。
  • 确定单例对象的生命周期与应用程序的生命周期相同。

3.懒汉式 

其核心特点是类实例在第一次使用时才创建,这种方式被称为“懒”初始化。懒汉式单例模式可以进一步细分为线程不安全的懒汉式和线程安全的懒汉式。

3.1线程不安全的懒汉式

这种实现方式的特点是在实例化对象时不进行同步处理,因此可能存在线程安全问题。

public class LazySingleton {private static LazySingleton instance;private LazySingleton() {}//没有加锁public static LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}
}

如果在单线程环境下运行,这种方式是简单且高效的。但在多线程环境下,如果两个线程如果同时检查instance是否为null,并且都发现它是null,那么它们都可能创建一个新的实例,这将导致instance变量引用多个实例,违反了单例模式的原则。

3.2线程安全的懒汉式

为了解决线程安全问题,可以在创建实例时进行同步处理。可以通过使用synchronized关键字来实现。

public class ThreadSafeLazySingleton {//防止内存可见性和重排序问题private static volatile ThreadSafeLazySingleton instance;private ThreadSafeLazySingleton() {}//通过加锁来保证线程安全public static synchronized ThreadSafeLazySingleton getInstance() {if (instance == null) {instance = new ThreadSafeLazySingleton();}return instance;}
}

在这个版本中,getInstance()方法被声明为synchronized,这意味着同一时间只有一个线程能够进入这个方法。volatile关键字确保了instance变量的读写操作对所有线程都是可见的,防止了内存中的变量值因线程内部缓存而出现不一致的情况。

优点:

  1. 延迟加载:实例在第一次使用时才会被创建,避免了资源浪费。
  2. 节省内存:只有在需要时才会创建实例,避免了一开始就创建对象的内存消耗。
  3. 适用于资源敏感型应用:适用于那些需要在应用启动时尽量减少资源占用的场景。

缺点:

  1. 线程不安全:在多线程环境下,可能会出现多个线程同时进入创建实例的逻辑,导致创建多个实例的问题。

  2. 需要额外考虑线程安全性:为了解决线程安全问题,需要在getInstance()方法上添加同步锁,会影响性能。

  3. 可能引起性能问题:由于需要在方法上添加同步锁,可能会导致性能下降,特别是在高并发环境下。

适用场景:

  • 资源敏感型应用:对资源占用有较高要求的应用,可以使用懒汉式单例来延迟实例化。

  • 单线程环境:在单线程环境下,懒汉式单例可以简单实现且没有线程安全问题。

  • 非高并发场景:在并发度不高的场景下,懒汉式单例可以满足需求。

4.双重检查式

双重检查锁定是一种优化的线程安全懒汉式实现,它通过两次检查实例是否已创建来减少同步的开销。

public class Singleton {// 声明,并用volatile修饰,保证在多线程环境下的有序性private volatile static Singleton instance = null;// 私有构造方法private Singleton () {}// 对外提供一个获取实例的方法,public static Singleton getInstance() {// 使用双重if检查, 降低锁竞争的频率if (instance == null) {// instance没有被实例化才去加锁synchronized (Singleton.class) {// 获取到锁后再检查一遍,确认instance没有被实例化后去真正的实例化if (instance == null) {instance = new Singleton();}}}return instance;}
}

在这个实现中,第一次检查instance是否为null是不加锁的,只有在instance确实为null时,才会进入同步块进行第二次检查和实例化。这种实现既保证了线程安全,又提高了性能,因为它避免了每次调用getInstance()时都进行同步。

优点:

  1. 性能优化:双重检查锁定的主要优点是它减少了每次获取单例实例时的同步开销。只有当实例尚未创建时,才会进行同步和创建实例。一旦实例被创建,后续的调用都不需要同步,从而提高了性能。

  2. 线程安全:通过在实例化过程中使用同步块,双重检查锁定确保了即使在多线程环境中,也只有一个实例被创建。

  3. 非强制性同步:与始终使用synchronized方法或块相比,双重检查锁定只在必要时进行同步,这避免了不必要的性能损失。

缺点:

  1. 复杂性:双重检查锁定的实现相对复杂,需要在代码中进行两次检查,容易引入错误和难以维护。

  2. 性能开销:虽然双重检查锁定可以减少加锁的频率,但仍需要进行两次检查,可能会引入性能开销。

使用场景:

双重检查锁定适用于以下场景:

  • 性能敏感的应用:当你需要频繁获取单例实例,并且实例化过程相对昂贵时,双重检查锁定可以提供更好的性能。

  • 多线程环境:在多线程环境中,你需要确保线程安全,同时希望减少同步的开销。

 总结:

单例模式是一种常用的设计模式,通过保证一个类只有一个实例,提供全局访问点,方便对实例进行管理。在Java中,可以通过不同的实现方式来创建单例对象,每种实现方式都有其优缺点。在应用中,需要根据实际情况选择合适的单例实现方式,并注意线程安全和内存管理问题。

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

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

相关文章

Tomcat安装部署及JavaEE项目创建

一.Tomcat下载 官网链接 选择自己需要的版本(本次采用Tomcat10)下载 二.文件结构 解压下载的文件,其主要文件目录如下: 三.启动Tomcat 找到bin目录下的startup.bat文件,双击运行 启动后出现弹窗&#xff0…

【数据结构】初识数据结构与复杂度总结

前言 C语言这块算是总结完了,那从本篇开始就是步入一个新的大章——数据结构,这篇我们先来认识一下数据结构有关知识,以及复杂度的相关知识 个人主页:小张同学zkf 若有问题 评论区见 感兴趣就关注一下吧 目录 1.什么是数据结构 2.…

Java 面向对象(基础)

1、面向对象的概述及两大要素:类与对象 1. 面向对象内容的三条主线: - Java类及类的成员:(重点)属性、方法、构造器;(熟悉)代码块、内部类 - 面向对象的特征:封装、继承…

洛谷 1331.海战

这道题其实对于那个船的相邻问题说的相当不清楚,因为既然不是一条船,为什么还相邻呢?让人有点摸不到头脑。 总之可以用dfs来解决。你也可以选择用bfs,这个模型本质上就是flood fill。 至于判断条件,也就是在一个#为中…

JMeter+Grafana+influxdb 配置出现transaction无数据情况解决办法

JMeterGrafanainfluxdb 配置出现transaction无数据情况解决办法 一、问题描述二、解决方法 一、问题描述 如下图所示出现application有数据但是transaction无数据情况 二、解决方法 需要做如下设置 打开变量设置如下图打开两个选项 然后再进行后端监听器的设置 如下图所…

Ansible批量更新远程主机用户密码 (包括Ansible批量做ssh互信)

按照集团运维信息安全制度, 需要每个一段时间对线上服务器密码进行一次变更,通过shell脚本部署比较繁琐,所以决定采用ansible脚本对远程主机root密码进行批量重置,该脚本已经在稳定运行在正式环境下。具体方法如下: 1) 在服务端安装ansible …

数据结构进阶篇 之 【交换排序】(冒泡排序,快速排序递归、非递归实现)

当你觉的自己不行时,你就走到斑马线上,这样你就会成为一个行人 一、交换排序 1.冒泡排序 BubbleSort 1.1 基本思想 1.2 实现原理 1.3 代码实现 1.4 冒泡排序的特性总结 2.快速排序 QuickSort 2.1 基本思想 2.2 递归实现 2.2.1 hoare版 2.2.2 …

软件设计原则:里氏替换原则

定义 里氏替换原则(Liskov Substitution Principle, LSP)确保继承表现为一种类型扩展而非类型的重定义。具体而言,如果类型 S 是类型 T 的子类型,则类型 T 的对象可以在程序中被类型 S 的对象替换(即,类型…

NoSQL之Redis

目录 一、关系型数据库与非关系型数据库 1.关系数据库 2.非关系数据库 2.1非关系型数据库产生背景 3.关系型数据库与非关系型数据区别 (1)数据存储方式不同 (2)扩展方式不同 (3)对事物性的支持不同 …

关于VueCli项目中如何加载调试Worker和SharedWorker

安装Webpack插件 VueCli 项目中默认是没有加载 worker 的配置,需要额外安装 webpack 插件来实现,让我们开始安装 worker-loader 插件 # npm npm install worker-loader # pnpm pnpm install worker-loader # yarn yarn add worker-loader配置Webpack插…

微服务(基础篇-008-es、kibana安装)

目录 05-初识ES-安装es_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1LQ4y127n4?p81&vd_source60a35a11f813c6dff0b76089e5e138cc 1.部署单点es 1.1.创建网络 1.2.加载镜像 1.3.运行 2.部署kibana 2.1.部署 2.2.DevTools 3.安装IK分词器 3.1.在线安装ik…

装修避坑指南 | 定制家具你遇到过哪些坑?福州中宅装饰,福州装修

定制家具时可能会遇到以下一些常见问题: 尺寸不准确:由于定制家具需要按需定制,对尺寸的要求很高。如果尺寸不准确,很可能会导致安装困难或者家具不符合空间需求。 材料质量差:有些厂家可能会使用质量较差的材料来降…

[AutoSar]BSW_Memory_Stack_003 NVM与APP的显式和隐式同步

目录 关键词平台说明背景一、implicit synchronization1.1 Write requests 流程 (NvM_WriteBlock)1.2 Read requests 流程 (NvM_ReadBlock)1.3 Restore default requests 流程 (NvM_RestoreBlockDefaults)1.4 Multi block read requests 流程 (NvM_ReadAll)1.5 Multi block wri…

C# BitConverter

BitConverter大端小端转16进制 BitConverter BitConverter 是 C# 中的一个类,它提供了用于字节顺序操作的方法,包括在基本数据类型(如 int、float、double 等)和它们的字节表示之间转换的方法。这个类在处理二进制数据、网络编程…

【Python系列】 yaml中写入数据

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

WEB漏洞-文件上传之基础及过滤方式

目录 案例1:百度搜索关键词,找到可能存在漏洞的网页 案例2:不同格式下的文件类型后门测试 案例3:配合解析漏洞下的文件类型后门测试 案例4:本地文件上传漏洞下的文件类型后门测试 案例5:某CVE漏洞利用…

Java基础知识总结(38)

(1)Arrays工具类 Java提供了Arrays工具类,里面包含了一些方法,可以直接操作数组。 1、int binarySearch(long[] a, long key):使用二分查找法查询key元素值在数组a中出现的索引,如果a数组不包含key元素&…

MySQL索引原理

MySQL索引原理 1、Innodb中的B树是怎么产生的呢? 背景1.1、mysql索引使用B树,为什么?1.2、主键索引图示1.3、mysql最好使用自增ID:为什么呢?1.4、高度为3的B树能存多少条数据?a、假设2层b、假设3层 2、索引采用什么数…

从0到1构建uniapp应用-store状态管理

背景 在 UniApp的开发中,状态管理的目标是确保应用数据的一致性,提升用户体验,并简化开发者的工作流程。通过合理的状态管理,可以有效地处理用户交互、数据同步和界面更新等问题。 此文主要用store来管理用户的登陆信息。 重要…

Dubbo入门项目搭建【Dubbo3.2.9、Nacos2.3.0、SpringBoot 2.7.17、Dubbo-Admin 0.6.0】

B站学习视频 基于Dubbo3.2.9、Nacos2.3.0、SpringBoot 2.7.17、Dubbo-Admin 0.6.0、Jdk1.8 搭建的Dubbo学习Demo 一、前置安装 1-1、Nacos 安装 我本地是通过docker-compose来安装nacos的,如果需要其它方式安装可以去百度找下教程,版本是2.3.0的 docker…