深入了解Java单例模式及其使用场景

什么是单例模式?

简单通俗来讲,单例模式是一种设计模式,它确保一个类只能有一个实例,并提供全局访问。在 Java 中,可以通过以下几种方式来实现线程安全的单例模式。

饿汉式单例

饿汉式单例模式指的是在类加载的时候就创建单例对象。这种方式的实现比较简单,只需要在类的定义中添加一个静态成员变量,该成员变量用于保存单例对象,同时将类的构造方法私有化,以防止外部创建对象。

public class Singleton {private static Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}
}

注意:

1. 饿汉式单例与懒汉式单例的区别在于对象的创建时间。饿汉式单例在类加载时就已经创建了单例对象,并且它是由static final 修饰的,保证了单例对象在整个程序中都只有一个实例,所以不会有性能问题,并且也不需要额外的同步操作来保证线程安全。所以饿汉式单例天生就是线程安全的。但是饿汉式单例在类加载时就创建了单例对象,因此在某些场景下可能会造成内存浪费。饿汉单例模式适用于单例对象创建开销不大,并且程序启动时需要使用单例对象的场景。

例如,在程序启动时就需要加载的配置信息,可以使用饿汉式单例来读取配置文件。因为在程序启动时就需要加载配置文件,而且配置文件只需要读取一次,所以可以使用饿汉式单例。

一个经典的应用场景就是在 Java 中的日志管理类,下面是一个简单的实现:

public class Logger {private static final Logger INSTANCE = new Logger();private Logger(){//初始化}public static Logger getInstance(){return INSTANCE;}public void log(String message){//执行日志记录}
}

这个Logger 类负责管理程序中所有的日志信息,因为在类加载时已经创建了 Logger 对象,所以在整个程序中只有一个 Logger 对象,并且是线程安全的。这样使用时只需要调用Logger.getInstance().log("log message")来记录日志。

还有一个特殊的场景就是在资源限制的情况下,比如设备硬件限制,或者是其他限制条件下,这种情况下因为不能等待长时间创建对象,又因为每个线程都可能会请求单例对象,所以此时就可以使用饿汉单例模式来保证程序在限制条件下的正常运行。

饿汉式单例模式虽然具有线程安全的优点,但在内存占用、类加载速度、扩展性和资源利用效率方面存在一些缺点。

  1. 类加载时初始化,占用空间:饿汉式单例模式在类加载时就完成了初始化,并创建了单例对象。这意味着无论是否需要使用该单例对象,都会占用一定的内存空间。如果单例对象的创建比较消耗系统资源,而外部一直没有调用该实例,那么这部分的系统资源消耗是没有意义的。

  2. 类加载较慢:由于饿汉式单例模式在类加载时就完成了初始化,这可能会导致类加载的速度相对较慢,尤其是在存在大量类需要加载的情况下。

  3. 扩展困难:饿汉式单例模式一般没有接口,扩展起来比较困难。如果要扩展单例对象的功能,只有修改代码这一途径,这不符合程序的开闭原则。

  4. 不支持延迟加载:饿汉式单例模式不支持延迟加载,即系统启动时就创建了单例对象,无论是否立即使用。这在某些情况下可能不是最优的资源利用方式。

懒汉式单例模式

懒汉式单例模式指的是在第一次使用时才创建单例对象。这种方式的实现相对复杂,需要注意线程安全问题,否则会导致多个线程同时创建多个对象。常见的解决方案是使用双重检查锁定。

public class Singleton {private static volatile Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}

上面的示例代码中,通过在 getInstance 方法中使用双重检查锁定来确保只有一个实例被创建。volatile 关键字可以避免指令重排序,以下是具体的说明。

instance = new Singleton()不是原子操作,这段代码可以简单分为下面三步执行:

  1.  为 instance 分配内存空间;

  2. 初始化 instance;

  3. 将 instance 指向分配的内存地址

由于但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2,造成未初始化完全的对象发布。

静态内部类单例模式

这种方式采用了类装载的机制来保证初始化实例时只有一个线程。类的静态属性只会在第一次加载类的时候初始化,在这里,JVM 帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。

public class Singleton {  private static class SingletonHolder {  private static final Singleton INSTANCE = new Singleton();  }  private Singleton () {}  public static final Singleton getInstance() {  return SingletonHolder.INSTANCE;  }  
}

将 instance 放在了内部类 SingletonHolder 中,前面我们提到饿汉式是类加载时就会立即创建对象,而静态内部类不会,它只会在调用了 getInstance 时,才会加载内部类 SingletonHolder,此时才会创建对象。利用静态内部类特点实现延迟加载,效率高。

枚举

这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。

enum Singleton{INSTANCE;public void say(){System.out.println("hello");}
}

在上面的示例中,Singleton被定义为一个枚举,其中只有一个元素INSTANCE,该元素代表了Singleton的唯一实例。可以通过Singleton.INSTANCE来访问这个唯一的实例。

单例模式的使用场景

  1. 需要确保系统中某个类只有一个实例存在的情况。比如,一个配置管理类,一个日志记录器等。
  2. 需要对某个类的实例进行严格控制,以确保在系统中始终只有一个实例存在。例如,线程池、数据库连接池等。
  3. 需要避免创建大量的实例,以节约系统资源的情况。例如,一个类的实例需要占用大量的系统资源,如果创建多个实例可能会导致系统崩溃或者性能下降。

单例模式虽然能够保证某个类的实例只有一个,但也会带来一些缺点。例如,单例模式会增加代码的复杂度,可能会导致代码的可测试性降低,因此需要在使用单例模式时慎重考虑。

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

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

相关文章

Redis位图和地理空间GEO介绍与使用

1、位图 应用场景 在很多互联网应用中,我们会存在签到送积分、签到领取奖励等这样的需求,比如: 签到1天送10积分,连续签到2天送20积分,3天送30积分,4天以上均送50积分等。 如果连续签到中断,则重置计数&…

伯克希尔·哈撒韦:“股神”的“登神长阶”

股价跳水大家见过不少,但一秒跌掉62万美元的你见过吗? 今天我们来聊聊“股市”巴菲特的公司——伯克希尔哈撒韦 最近,由于纽交所技术故障,伯克希尔哈撒韦A类股股价上演一秒归“零”,从超过62万美元跌成185.1美元&…

关于main函数参数列表的那些事

写在最前面: 本篇博客所写代码,全部都依赖于Linux环境。 在开始之前,我们先问自己几个问题: main函数可以传参吗?如果main函数可以传参,最多可以传几个参数。main函数传递的参数具体作用是什么? 一.是否…

C++之类与类之间的关系

1、UML 2、继承(泛化) 3、关联 一个类对象与另一个类对象存在一个固定关系。他们的关系不是暂时的,而是固定的。 一个类对象作为另一个类对象的成员。例如订单,是用户的一个成员。用户关联订单。 4、聚合 聚合其实是特殊的一种…

渗透测试之内核安全系列课程:Rootkit技术初探(二)

今天,我们来讲一下内核安全! 本文章仅提供学习,切勿将其用于不法手段! 目前,在渗透测试领域,主要分为了两个发展方向,分别为Web攻防领域和PWN(二进制安全)攻防领域。在…

[office] 16种常见的COUNTIF函数公式设置 #笔记#职场发展

16种常见的COUNTIF函数公式设置 1、返回包含值12的单元格数量 COUNTIF(A:A,12) 2、返回包含负值的单元格数量 COUNTIF(A:A,"<0") 3、返回不等于0的单元格数量 COUNTIF(A:A,"<>0") 4、返回大于5的单元格数量 COUNTIF(A:A,">5"…

中国新闻网怎么投稿 新闻稿件文章如何发布到中国新闻网上,附中国新闻网价格明细

中国新闻网是中国最具影响力和权威性的新闻门户网站之一。作为广大作者和媒体从业者&#xff0c;怎样向中国新闻网投稿一直是一个备受关注的话题。在这篇文章中&#xff0c;我们将着重介绍媒介库网发稿平台&#xff0c;并分享如何在该平台上成功投稿至中国新闻网。 媒介库网发稿…

深度学习中tensorflow和pytorch框架有什么不同,该如何选择

Tensorflow与Pytorch的选择 1. 编程风格和易用性2. 社区和生态系统3. 性能和优化4. 选择建议 TensorFlow 和 PyTorch 是两种流行的深度学习框架&#xff0c;各有优缺点和特定的使用场景。以下是它们的主要区别以及选择时需要考虑的因素&#xff1a; 1. 编程风格和易用性 Tenso…

IDEA 2022

介绍 【尚硅谷IDEA安装idea实战教程&#xff08;百万播放&#xff0c;新版来袭&#xff09;】 jetbrains 中文官网 IDEA 官网 IDEA 从 IDEA 2022.1 版本开始支持 JDK 17&#xff0c;也就是说如果想要使用 JDK 17&#xff0c;那么就要下载 IDEA 2022.1 或之后的版本。 公司…

二叉树的非递归后序遍历

二叉树的后序遍历是一种深度优先遍历算法&#xff0c;其遍历顺序为&#xff1a;左子树 -> 右子树 -> 根节点。非递归实现后序遍历通常使用一个辅助栈来模拟递归过程。 以下是使用C实现二叉树非递归后序遍历的示例代码&#xff1a; #include <iostream> #include …

C语言.数据结构.双向链表

数据结构.双向链表 1.双向链表的结构1.1链表的简单介绍1.2图文分析 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.…

CMMI软件能力成熟度评估标准

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl CMMI概述 CMMI&#xff0c;全称为Capability Maturity Model Integration&#xff0c;即能力成熟度模型集成&#xff0c;是在原有的CMM&#xff08;Capability Maturity Mo…

一文吃透!如何在鸿蒙上开发Unity游戏的方法!

实际效果&#xff1a; 使用 Webview 在鸿蒙设备上运行 Unity 游戏需要几个步骤&#xff1f; 用 Webview 在鸿蒙上运行 Unity 游戏 ①创建鸿蒙全屏工程 在 DevEco 中创建一个新工程&#xff0c;模板选择 Full Screen Ability。 ②添加 Webview 这个工程的布局很简单&#xf…

JavaScript中获取时间戳的方法

在 JavaScript 中获取时间戳的方法有多种&#xff0c;具体如下&#xff1a; 1.使用 Date 对象的 getTime() 方法获取当前时间的时间戳&#xff1a; var timestamp new Date().getTime(); 2.使用 Date 对象的 valueOf() 方法获取当前时间的时间戳&#xff1a; var timestamp n…

esp32-c6所有配套教程

1.介绍 本文是esp32-c6所有资料的介绍 如果需要详细代码的话请访问下面这个链接 esp32-c6使用教程wifi&#xff08;espidf修改成arduino&#xff09;附带代码websocket&#xff0c;舵机&#xff0c;点灯【2024年】-CSDN博客 配置环境 视频教程 0-2设置开发环境_哔哩哔哩_bi…

策略模式+简单工厂

&#x1f347;工厂模式 &#x1f348;工厂模式向策略模式过度——工厂加一个保安 &#x1f34f;策略模式 &#x1f350;策略模式简单工厂 声明本文需要理解多态的基础上才能来学习 欢迎前来学习——继承和多态 学习记录 工厂模式 需要什么就生成什么 // 工厂模式 class Fact…

Flink任务如何跑起来之 1.DataStream和Transformation

Flink任务如何跑起来之 1.DataStream和Transformation 1. 滥觞 在使用Flink完成业务功能之余&#xff0c;有必要了解下我们的任务是如何跑起来的。知其然&#xff0c;知其所以然。 既然重点是学习应用程序如何跑起来&#xff0c;那么应用程序的内容不重要&#xff0c;越简单…

好家风短视频:成都鼎茂宏升文化传媒公司

好家风短视频&#xff1a;传承与发扬家庭美德的新载体 在数字时代的浪潮中&#xff0c;短视频以其简短、生动、直观的特点&#xff0c;成为了人们获取信息、传递情感的重要渠道。成都鼎茂宏升文化传媒公司而在这个多元化的内容生态中&#xff0c;好家风短视频以其独特的价值和…

5.透明效果

实时渲染中要实现透明效果&#xff0c;通常会在渲染模型时控制它的透明通道&#xff08;Alpha channel&#xff09;。 当一个物体被渲染到屏幕上时&#xff0c;每个片元除了颜色和深度值之外&#xff0c;它还有另一个属性—透明度。 当透明度为1时&#xff0c;表示该像素是完…

Dvws靶场

文章目录 一、XXE外部实体注入二、No-SQL注入三、Insecure Direct Object Reference四、Mass Assignment五、Information Disclosure六、Command Injection七、SQL注入 一、XXE外部实体注入 访问http://192.168.92.6/dvwsuserservice?wsdl&#xff0c;发现一个SOAP服务。在SO…