当前位置: 首页 > news >正文

Java设计模式——单例模式(特性、各种实现、懒汉式、饿汉式、内部类实现、枚举方式、双重校验+锁)

文章目录

    • 单例模式1️⃣
      • 特性💪
      • 单例模式的类型与实现:
        • 类型
        • 懒汉式实现(线程不安全)
        • 懒汉式实现(线程安全)
        • 双重锁校验懒汉式(线程安全)
        • 饿汉式实现(线程安全)
        • 使用类的内部类实现⭐
        • 枚举方式实现单例(推荐)👍

单例模式1️⃣

单例模式是指在内存中只会创建且仅创建一次对象的设计模式。让你能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点。

特性💪

  1. 唯一性:整个系统中,单例类只能有一个实例。
  2. 私有化构造函数:防止外部通过new关键字直接创建对象。
  3. 静态方法提供全局访问点:通常使用getInstance()方法来获取该类的唯一实例。
  4. 延迟实例化(懒加载):有些实现会在第一次调用getInstance()时才创建实例,以节省资源。

单例模式的类型与实现:

类型
  1. 懒汉式:在真正需要使用对象时才去创建该单例类对象
  2. 饿汉式:在类加载时已经创建好该单例对象,等待被程序使用
懒汉式实现(线程不安全)

只有当调用getInstance()方法时才会创建单例对象,这种方式实现了延迟加载,但是需要考虑多线程环境下的线程安全问题。

img

public class Singleton {private static Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
  • 单例模式不允许外部直接创建,所以构造函数添加私有属性private
  • 这种方式满足懒汉式,但是在并发场景下,多个线程使用单例对象可能导致实例并存,从而违反了单例要求
懒汉式实现(线程安全)

上述懒汉式实现是线程不安全的(例如同时两个线程去获取单例对象,如果此时单例对象还未创建,可能会导致同事创建两个对象,从而违反单例),故我们要解决线程安全问题。

img

最容易想到的方法:使用锁(synchronized)给类加锁来保证线程安全

public class Singleton {private static Singleton instance;private Singleton() {}public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
双重锁校验懒汉式(线程安全)

但是上述方法每次获取对象的时候都要去先获取锁,并发性能不是很好

可以进行优化:(如果没有实例化则加锁创建,如果实例化了则直接获取,可以使得已经实例化的单例对象在获取单例对象时无需先获取锁,而是直接获取对象)

使用Double Check(双重校验) + Lock(加锁) 的写法:

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;}
}

同时我们也使用了volatile关键字去确保instance变量的更新对所有线程都是立即可见的,并且禁止指令重排序,保证多线程环境下的正确性

  1. 防止指令重排序: 在多线程环境下,JVM为了优化性能可能会对指令进行重排序。对于单例对象的创建而言,构造函数内部的操作可能被重排序到对象引用赋值之后。例如,如果一个线程正在执行实例化操作,它可能会先将对象引用设置为非空(即指向一块内存),然后再完成对象的初始化。这种情况下,另一个线程可能看到的是一个部分初始化的对象(因为对象的引用不是null了),这会导致不可预测的行为或错误。volatile关键字可以禁止这种指令重排序,保证对象完全初始化之后才会被其他线程看到。
  2. 可见性保证volatile关键字确保了一个线程对共享变量(在这个场景下是单例对象的引用)的修改对于其他线程是立即可见的。也就是说,当一个线程成功创建了单例对象后,所有其他线程都能看到这个对象已经被正确地初始化了,而不会读取到旧的或者默认的值(如null)。这避免了多个线程同时创建多个实例的情况。
饿汉式实现(线程安全)

在类加载的时候就创建好单例对象,这种方式简单但不够灵活,因为它不能做到延迟加载。

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

在类加载的时候,private static final Singleton singleton = new Singleton();这行代码已经实例化好了一个单例对象在内存中。

使用类的内部类实现⭐

利用了Java语言的类加载机制,只有当调用getInstance()方法时,内部类才会被加载,从而实现了懒加载和线程安全,同时不会因为加锁的方式耗费性能。

public class Singleton {private Singleton() {}private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}
  • 这主要是因为JVM虚拟机可以保证多线程并发访问的正确性,也就是一个类的构造方法在多线程环境下可以被正确的加载。
  • 此种方式也是非常推荐使用的一种单例模式
枚举方式实现单例(推荐)👍

在Java中,使用枚举(Enum)来实现单例模式是一种非常简洁且高效的方法。枚举类型的单例不仅能够防止反射攻击和序列化导致的重复实例化问题,而且代码量极少,易于理解和维护。这是因为Java的枚举机制保证了每个枚举常量的唯一性,并且在类加载时自动初始化

public enum Singleton {INSTANCE;private final String property;// 初始化属性值Singleton() {this.property = "Some Value";}public String getProperty() {return property;}// 其他业务方法public void doSomething() {// 方法逻辑...}
}public class Client {public static void main(String[] args) {Singleton singleton = Singleton.INSTANCE;singleton.doSomething();System.out.println(singleton.getProperty());}
}

枚举单例的优点:

  1. 天然线程安全:由于枚举常量在类加载时就被初始化,所以不需要额外的同步代码来保证线程安全。
  2. 防止反序列化创建新实例:枚举类型具有内在的序列化机制,如果尝试反序列化一个枚举类型的对象,它总是返回现有的枚举常量,而不会创建新的实例。
  3. 防止反射攻击:即使通过反射调用私有构造函数,也无法创建新的枚举实例。
  4. 简洁:相比于其他单例模式的实现方式,枚举单例的代码更加简洁明了。
  5. 延迟加载:虽然枚举类型不是天生支持懒加载,但是可以通过将实际的工作委托给另一个静态内部类来实现这一点。

相关文章:

Java设计模式——单例模式(特性、各种实现、懒汉式、饿汉式、内部类实现、枚举方式、双重校验+锁)

文章目录 单例模式1️⃣特性💪单例模式的类型与实现:类型懒汉式实现(线程不安全)懒汉式实现(线程安全)双重锁校验懒汉式(线程安全)饿汉式实现(线程安全)使用类的内部类实现⭐枚举方式实现单例(推荐)👍 单例…...

数字普惠金融对新质生产力的影响研究(2015-2023年)

基于2015—2023年中国制造业上市公司数据,探讨了数字普惠金融对制造业企业新质生产力的影响及作用机理。研究发现,数字普惠金融有助于促进制造业企业新质生产力的发展,尤其是在数字普惠金融的使用深度较大的情况下,其对新质生产力…...

国产编辑器EverEdit - 扩展脚本:新建同类型文件(避免编程学习者反复新建保存练习文件)

1 扩展脚本:在当前文件目录下新建同类型文件 1.1 应用场景 用户在进行编程语言学习时,比如:Python,经常做完一个小练习后,又需要新建一个文件,在新建文件的时候,不但要选择文件类型&#xff0c…...

jupyter notebook练手项目:线性回归——学习时间与成绩的关系

线性回归——学习时间与学习成绩的关系 第1步:导入工具库 pandas——数据分析库,提供了数据结构(如DataFrame和Series)和数据操作方法,方便对数据集进行读取、清洗、转换等操作。 matplotlib——绘图库,p…...

dockerfile2.0

dockerfile实现lnmp nginx centos7 mysql centos7 php centos7 自定义镜像来实现整个架构 cd /opt mkdir nginx mysql php cd nginx 拖入nginx和wordpress vim Dockerfile vim nginx.conf ↓ worker_processes 1; events {worker_connections 1024; } http {include …...

【spring mvc】文件上传、下载

文件上传,存储至本地目录中 一、代码1、工具类(敏感后缀过滤)2、文件上传,存储至本地3、文件下载 二、效果演示1、上传1.1、postMan 请求1.2、上传效果 2、下载2.1、下载效果 一、代码 1、工具类(敏感后缀过滤&#x…...

FPGA工程师成长四阶段

朋友,你有入行三年、五年、十年的职业规划吗?你知道你所做的岗位未来该如何成长吗? FPGA行业的发展近几年是蓬勃发展,有越来越多的人才想要或已经踏进了FPGA行业的大门。很多同学在入行FPGA之前,都会抱着满腹对职业发…...

java fastjson2 解析JSON用法解析

Fastjson2 是 Fastjson 的升级版本,提供了更好的性能和扩展性,同时也在 API 和功能上做了很多改进。使用 Fastjson2 解析 JSON 数据非常简单,支持多种方式来解析 JSON 字符串、嵌套 JSON 对象和数组、以及转换成 Java 对象。下面详细介绍 Fas…...

计算机视觉算法实战——步态识别(主页有源码)

✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ ​ ​​​​​​​​​​​​​​​​​​ 1. 步态识别简介✨✨ 步态识别(Gait Recognition)是计算机视觉领域中的一个…...

LabVIEW水位监控系统

LabVIEW开发智能水位监控系统通过集成先进的传感技术与控制算法,为工业液体存储提供精确的水位调控,保证了生产过程的连续性与安全性。 项目背景 在化工和饮料生产等行业中,水位控制的准确性对保证生产安全和提高产品质量至关重要。传统的水…...

网络层协议-----IP协议

目录 1.认识IP地址 2.IP地址的分类 3.子网划分 4.公网IP和私网IP 5.IP协议 6.如何解决IP地址不够用 1.认识IP地址 IP 地址(Internet Protocol Address)是指互联网协议地址。 它是分配给连接到互联网的设备(如计算机、服务器、智能手机…...

计算机网络八股文学习笔记

总结来自于javaguide,本文章仅供个人学习复习 javaguide计算机网络八股 文章目录 计算机网络基础网络分层模型OSI七层模型TCP/IP四层模型 HTTP从输入URL到页面展示到底发生了什么?(非常重要)HTTP状态码HTTP Header中常见的字段有哪些?HTTP和HTTPS有什么区别?(重要)HTTP/1.0和…...

IntelliJ IDEA中Maven项目的配置、创建与导入全攻略

大家好,我是袁庭新。 IntelliJ IDEA是当前最流行的Java IDE(集成开发环境)之一,也是业界公认最好用的Java开发工具之一。IntelliJ IDEA支持Maven的全部功能,通过它我们可以很轻松地实现创建Maven项目、导入Maven项目、…...

如何在Jupyter中快速切换Anaconda里不同的虚拟环境

目录 介绍 操作步骤 1. 选择环境,安装内核 2. 注册内核 3. 完工。 视频教程 介绍 很多网友在使用Jupyter的时候会遇到各种各样的问题,其中一个比较麻烦的问题就是我在Anaconda有多个Python的环境里面,如何让jupyter快速切换不同的Pyt…...

stack和queue专题

文章目录 stack最小栈题目解析代码 栈的压入弹出序列题目解析代码 queue二叉树的层序遍历题目解析代码 stack stack和queue都是空间适配器 最小栈 最小栈的题目链接 题目解析 minst是空就进栈&#xff0c;或者是val < minst.top()就进栈 代码 class MinStack { public:M…...

【Vue】点击侧边导航栏,右侧main对应显示

需求&#xff1a;点击侧边导航栏&#xff0c;右侧main对应显示 通过v-if或v-show等指令来控制不同内容的显示隐藏来实现 注意&#xff1a; 使用v-if时候进行导航栏切换&#xff0c;右侧显示区域可能会出现样式错乱&#xff1b;使用v-show则不会出现此错误 <template>&…...

【Debug】django.db.utils.OperationalError: (1040, ‘Too many connections‘)

报错&#xff1a; django.db.utils.OperationalError: (1040, ‘Too many connections‘) 排查 可能是Mysql的连接数量超过了允许的最大连接数量&#xff1b; 查看Mysql允许最大连接数量&#xff1a; -- 查看允许连接的最大数量 SHOW VARIABLES LIKE %max_connections%;-- 查…...

如何开放2375和2376端口供Docker daemon监听

Linux (以 Ubuntu 为例) 1. 修改 Docker 配置文件 打开 Docker 的配置文件 /etc/docker/daemon.json。如果该文件不存在&#xff0c;则可以创建一个新的。 bash sudo nano /etc/docker/daemon.json在配置文件中添加以下内容&#xff1a; json {"hosts": ["un…...

RabbitMQ确保消息可靠性

消息丢失的可能性 支付服务先扣减余额和更新支付状态&#xff08;这俩是同步调用&#xff09;&#xff0c;然后通过RabbitMq异步调用支付服务更新订单状态。但是有些情况下&#xff0c;可能订单已经支付 &#xff0c;但是更新订单状态却失败了&#xff0c;这就出现了消息丢失。…...

前端常见的设计模式之【单例模式】

前端常见的设计模式&#xff1a; 单例模式观察者模式工厂模式适配器模式装饰器模式命令模式迭代器模式组合模式策略模式发布订阅模式 单例模式【创建型设计模式】&#xff1a; 单例模式是确保一个类只有一个实例&#xff0c;并提供一个全局访问点。这个模式非常适合那些需要…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

大学生职业发展与就业创业指导教学评价

这里是引用 作为软工2203/2204班的学生&#xff0c;我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要&#xff0c;而您认真负责的教学态度&#xff0c;让课程的每一部分都充满了实用价值。 尤其让我…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

莫兰迪高级灰总结计划简约商务通用PPT模版

莫兰迪高级灰总结计划简约商务通用PPT模版&#xff0c;莫兰迪调色板清新简约工作汇报PPT模版&#xff0c;莫兰迪时尚风极简设计PPT模版&#xff0c;大学生毕业论文答辩PPT模版&#xff0c;莫兰迪配色总结计划简约商务通用PPT模版&#xff0c;莫兰迪商务汇报PPT模版&#xff0c;…...

提升移动端网页调试效率:WebDebugX 与常见工具组合实践

在日常移动端开发中&#xff0c;网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时&#xff0c;开发者迫切需要一套高效、可靠且跨平台的调试方案。过去&#xff0c;我们或多或少使用过 Chrome DevTools、Remote Debug…...