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

一、单例模式

文章目录

  • 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 实现的要点

  1. 构造器私有化。例如 private Singleton() {}
  2. 类中有一个 静态 的单例属性。
  3. 提供一个 静态 方法来获取上述单例。

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 类图

alt text
其中,singleton 属性和 Singleton() 构造器都是 私有的,而 getInstance() 方法是 公开的。此外,singleton 属性和 getInstance() 方法都是 静态的

6.2 角色

在单例模式中,只有一个角色 Singleton,它负责 实现返回单例的 静态 方法

7 推荐的单例模式的实现

  1. 饿汉式
  2. 双重检查
  3. 静态内部类
  4. 枚举

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 …...

Ubuntu系统下交叉编译openssl

一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

微信小程序之bind和catch

这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...

visual studio 2022更改主题为深色

visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包

文章目录 现象&#xff1a;mysql已经安装&#xff0c;但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时&#xff0c;可能是因为以下几个原因&#xff1a;1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

docker 部署发现spring.profiles.active 问题

报错&#xff1a; org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...