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

设计模式 - Singleton pattern 单例模式

文章目录

  • 定义
  • 单例模式的实现构成
    • 构成
    • UML图
  • 单例模式的六种实现
    • 懒汉式-线程不安全
    • 懒汉式-线程安全
    • 饿汉式-线程安全
    • 双重校验锁-线程安全
    • 静态内部类实现
    • 枚举实现
  • 总结
  • 其他设计模式文章:
  • 最后

定义

在这里插入图片描述

单例模式是一种创建型设计模式,它用来保证一个类只有一个实例, 并且提供一个访问该实例的全局节点。其在很多场景中都有应用,比如数据库连接池、日志记录器、Spring中对象的创建等。

总的来说,单例模式在需要控制实例数量、确保全局唯一性的场景中被广泛应用。单例模式通过限制类的实例化对象为一个,可以确保全局唯一性的场景中被广泛应用,从而有助于控制资源访问、简化全局访问点、减少内存占用等,在很多情况下都可以提升程序的运行效率。

单例模式的实现构成

构成

一个私有的构造函数、一个私有的静态变量以及一个共有的静态函数。

其中,私有构造函数保证了其他线程不能通过new来创建对象实例,而共有的静态函数则是用来后续所有对此函数的调用都返回唯一的私有静态变量。

UML图

在这里插入图片描述

单例模式的六种实现

懒汉式-线程不安全

下面实现中,instance 被延迟实例化,这样的话,当没有使用到这个类的话,就会节约资源,不会实例化 LazySingletonsAreNotSafe

但是该实现是线程不安全的,因为在多线程环境下,可以有多个线程同时进入 getInstance 方法,并且这个时候 instance 还未实例化,那么它们就都可以进入到 if 逻辑中,执行实例化操作,从而导致线程不安全问题。

public class LazySingletonsAreNotSafe {private static LazySingletonsAreNotSafe instance;private LazySingletonsAreNotSafe() {}public static LazySingletonsAreNotSafe getInstance() {if (instance == null) {instance = new LazySingletonsAreNotSafe();}return instance;}
}

懒汉式-线程安全

那么,如何可以保证线程安全呢?

其实,上一个实现方式中,线程不安全就是因为 instance 的实例化被执行了很多次,所以我们只要对 getInstance 方法进行加锁,保证同一个时间点只有一个线程可以进入该方法进行实例化操作,那么就保证了线程安全问题。
实现代码如下:

public class LazySingletonsAreSafe {private static LazySingletonsAreSafe instance;private LazySingletonsAreSafe() {}// 关键点:synchronized进行了加锁操作,从而保证线程安全。public static synchronized LazySingletonsAreSafe getInstance() {if (instance == null) {instance = new LazySingletonsAreSafe();}return instance;}
}

饿汉式-线程安全

对于懒汉式方法,如果不加锁会导致线程安全问题,而加锁虽然会保证线程安全,但是也带来了一定程度上的性能损耗,因此可以采用饿汉式。
懒汉式线程安全问题的原因是 getInstance 方法可能被执行多次,从而导致被实例化多次。所以我们采用在类加载的时候,直接实例化 instance ,这样就会避免实例化多次的问题。

当然,因为我们一开始在类加载的时候对象就被实例化了,所以也不会有延迟实例化种可以节约资源的优点。

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

双重校验锁-线程安全

双重校验锁先判断 uniqueInstance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁。

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

问题1: 为什么两个if?

if (instance == null) {synchronized (DoubleCheckedLockingSingleton.class) {if (instance == null) {instance = new DoubleCheckedLockingSingleton();}}}

第一个if是因为:高并发场景下,还是可能有不止一个线程成功的在 instance 还未初始化的时候就进入这里了,所以他们都会走下面的逻辑,所以加了一把锁,用来保证线程安全问题。
而第二个if则是因为:等到第一个线程执行完实例化之后,它会释放锁,这样的话下一个线程就会来拿这把锁,然后进行新一轮的实例化。所以,在锁里添加了第二个if用来进行判断,避免实例化多次。

问题2: 为什么 instancevolatile 进行修饰?

private static volatile DoubleCheckedLockingSingleton instance;

这个是因为 volatile 有禁止指令重排的功能。上述代码中单例对象有的时候可能会发生空指针异常的问题。

对于instance = new DoubleCheckedLockingSingleton(); 它其实是分为三个步骤来执行的:

  1. JVM为对象分配内存
  2. 在内存中进行对象的初始化
  3. 将内存对应的地址复制给instance

假设,现在有两个线程进入到了getInstance方法,当T1线程执行实例化操作时,T2线程在进行判断。

因为instance = new DoubleCheckedLockingSingleton();操作不是原子的,所以编译器可能会进行指令的重排序,即:

  1. JVM为对象分配内存
  2. 将内存对应的地址复制给instance
  3. 在内存中进行对象的初始化

这样的话,当T1线程执行完第二步地址复制给instance的时候,T2线程去进行判断,那么instance == null则是为true,所以会直接跳到最下面 return instance。从而导致空指针问题。

volatile可以避免指令重排,所以只要用volatile修饰instance就可以避免这个问题了。
在这里插入图片描述

静态内部类实现

BillPughSingleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 getUniqueInstance 方法从而触发 SingletonHolder.INSTANCESingletonHolder才会被加载,进行初始化。

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

枚举实现

枚举实例的创建是线程安全的,而且在任何情况下都是它一个单例。在别的几种单例中,反序列化时会重新创建对象,而枚举单例则不存在这种情况。

public enum EnumSingleton {INSTANCE;public void someMethod() {}
}

总结

      
1. 饿汉式

    实现:在类加载时就完成了实例化。
    特点:线程安全,实现简单;但可能会造成资源浪费,因为即使不需要使用实例,也会在类加载时创建。
      
2. 懒汉式

    实现:在第一次调用 getInstance() 方法时进行实例化。
    特点:延迟加载,节省资源;但需要在 getInstance() 方法上加锁才可以保证线程安全,会影响性能。
      
3. 双重校验锁

    实现:在 getInstance() 方法中加入两次实例检查,第二次检查前加上锁,既保证了线程安全又提高了效率。
    特点:结合了懒汉式和饿汉式的优点,既实现了延迟加载,又优化了并发性能。
      
4. 静态内部类

    实现:将单例实例放在静态内部类中,当外部类被加载时静态内部类并不会被加载,只有在首次调用 getInstance() 方法时才会加载。
    特点:既实现了延迟加载,又保证了线程安全,且不需显式同步。
      
5. 枚举

    实现:利用枚举类型的特性来保证实例的唯一性。
    特点:线程安全,简洁易读,还能防止反序列化攻击。

其他设计模式文章:

  • 设计模式 - Singleton pattern 单例模式
  • 设计模式 - Factory Method 工厂方法
  • 设计模式 - Chain Of Responsibility 责任链模式
  • 设计模式 - Template Method 模板方法
  • 设计模式 - Strategy Pattern策略模式
  • 设计模式 - Observer Pattern 观察者模式

最后

如果小伙伴们觉得我写的文章不错的话,那么请给我点点关注,我们下次见!
      在这里插入图片描述

相关文章:

设计模式 - Singleton pattern 单例模式

文章目录 定义单例模式的实现构成构成UML图 单例模式的六种实现懒汉式-线程不安全懒汉式-线程安全饿汉式-线程安全双重校验锁-线程安全静态内部类实现枚举实现 总结其他设计模式文章:最后 定义 单例模式是一种创建型设计模式,它用来保证一个类只有一个实…...

显示学习5(基于树莓派Pico) -- 彩色LCD的驱动

和这篇也算是姊妹篇,只是一个侧重SPI协议,一个侧重显示驱动。 总线学习3--SPI-CSDN博客 驱动来自:https://github.com/boochow/MicroPython-ST7735 所以这里主要还是学习。 代码Init def __init__( self, spi, aDC, aReset, aCS) :"&…...

ros vscode配置gdb调试

ros工程vscode下配置gdb的调试环境需要添加几个配置文件,下面贴一下用得到的几个配置文件。 c_cpp_properties.json,这个配置作用是方便代码跳转。 {"configurations": [{"browse": {"databaseFilename": "${defau…...

C 环境设置

C 环境设置 C语言作为一种广泛使用的编程语言,其环境设置是每个开发者必须掌握的基本技能。本文将详细介绍如何在不同的操作系统上设置C语言开发环境,包括Windows、macOS和Linux系统。我们将涵盖安装编译器、配置开发环境以及编写和运行第一个C程序。 Windows系统上的C环境…...

Linux-ubuntu操作系统装机步骤

1、下载iso镜像 方法一、访问Ubuntu官网 方法二、163镜像 2、制作U盘启动盘 方法一、UltraISO(软碟通)写入硬盘映像,参考该 [链接] 方法二、Rufus,参考该 [链接] 3、安装 参考该 [链接] 4、相关配置 Ubuntu 换源 参考链接…...

马尔科夫毯:信息屏障与状态独立性的守护者

马尔科夫毯(Markov Blanket)是概率图模型中的一个重要概念,用于描述某一节点在网络中的信息独立性和条件依赖关系。马尔科夫毯定义了一个节点的“信息屏障”,即给定马尔科夫毯中节点的状态,该节点与网络中其他节点的状…...

Pandas的30个高频函数使用介绍

Pandas是Python中用于数据分析的一个强大的库,它提供了许多功能丰富的函数。本文介绍其中高频使用的30个函数。 read_csv(): 从CSV文件中读取数据并创建DataFrame对象。 import pandas as pd df pd.read_csv(data.csv) read_excel(): 从Excel文件中读取数据…...

1. protobuf学习

文章目录 1. protobuf介绍1.1 ProtoBuf使用场景说明2. 其他序列化介绍2.1 Json2.1.1 使用Json序列化2.1.2 Json反序列化2.2 其他可选地序列化和反序列化3. protoBuf3.1 protobuf数据类型3.2 protobuf使用步骤3.2.1 定义proto文件3.2.2 编译proto文件3.2.2.1 安装protocol buffe…...

Java面试题:SpringBean的生命周期

SpringBean的生命周期 BeanDefinition Spring容器在进行实例化时,会将xml配置的信息封装成BeanDefinition对象 Spring根据BeanDefinition来创建Bean对象 包含很多属性来描述Bean 包括 beanClassName:bean的类名,通过类名进行反射 initMethodName:初始化方法名称 proper…...

50 IRF检测MAD-BFD

IRF 检测MAD-BFD IRF配置思路 网络括谱图 主 Ten-GigabitEthernet 1/0/49 Ten-GigabitEthernet 1/0/50 Ten-GigabitEthernet 1/0/51 备 Ten-GigabitEthernet 2/0/49 Ten-GigabitEthernet 2/0/50 Ten-GigabitEthernet 2/0/51 1 利用console线进入设备的命令行页…...

SpringSecurity-1(认证和授权+SpringSecurity入门案例+自定义认证+数据库认证)

SpringSecurity 1 初识权限管理1.1 权限管理的概念1.2 权限管理的三个对象1.3 什么是SpringSecurity 2 SpringSecurity第一个入门程序2.1 SpringSecurity需要的依赖2.2 创建web工程2.2.1 使用maven构建web项目2.2.2 配置web.xml2.2.3 创建springSecurity.xml2.2.4 加载springSe…...

Java高级

类变量/静态变量package com.study.static_; 通过static关键词声明,是该类所有对象共享的对象,任何一个该类的对象去访问他的时候,取到的都是相同的词,同样任何一个该类的对象去修改,所修改的也是同一个对象. 如何定义及访问? 遵循相关访问权限 访问修饰符 static 数据类型…...

python实现图像分割算法3

python实现区域增长算法 算法原理基本步骤数学模型Python实现详细解释优缺点应用领域区域增长算法是一种经典的图像分割技术,它的目标是将图像划分为多个互不重叠的区域。该算法通过迭代地合并与种子区域相似的邻域像素来实现分割。区域增长算法通常用于需要精确分割的场景,如…...

解密XXE漏洞:原理剖析、复现与代码审计实战

在网络安全领域,XML外部实体(XXE)漏洞因其隐蔽性和危害性而备受关注。随着企业对XML技术的广泛应用,XXE漏洞也逐渐成为攻击者们利用的重点目标。一个看似无害的XML文件,可能成为攻击者入侵系统的利器。因此&#xff0c…...

Spring Boot集成Resilience4J实现限流/重试/隔离

1.前言 上篇文章讲了Resilience4J实现熔断功能&#xff0c;文章详见&#xff1a;Spring Boot集成Resilience4J实现断路器功能 | Harries Blog™&#xff0c;本篇文章主要讲述基于Resilience4J实现限流/重试/隔离。 2.代码工程 pom.xml <dependency><groupId>io…...

谷粒商城实战笔记-119~121-全文检索-ElasticSearch-mapping

文章目录 一&#xff0c;119-全文检索-ElasticSearch-映射-mapping创建1&#xff0c;Elasticsearch7开始不支持类型type。2&#xff0c;mapping2.1 Elasticsearch的Mapping 二&#xff0c;120-全文检索-ElasticSearch-映射-添加新的字段映射三&#xff0c;121-全文检索-Elastic…...

Java 并发编程:Java 线程池的介绍与使用

大家好&#xff0c;我是栗筝i&#xff0c;这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 024 篇文章&#xff0c;在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验&#xff0c;并希望进…...

ubuntu上安装HBase伪分布式-2024年08月04日

ubuntu上安装HBase伪分布式-2024年08月04日 1.HBase介绍2.HBase与Hadoop的关系3.安装前言4.下载及安装5.单机配置6.伪分布式配置 1.HBase介绍 HBase是一个开源的非关系型数据库&#xff0c;它基于Google的Bigtable设计&#xff0c;用于支持对大型数据集的实时读写访问。HBase有…...

Mojo的特征与参数(参数化部分)详解

许多语言都具有元编程功能:即编写生成或修改代码的代码。Python 具有动态元编程功能:装饰器、元类等功能。这些功能使 Python 非常灵活且高效,但由于它们是动态的,因此会产生运行时开销。其他语言具有静态或编译时元编程功能,如 C 预处理器宏和 C++ 模板。这些功能可能受到…...

C++数组、vector求最大值最小值及其下标

使用 <algorithm> 头文件来查找数组或向量中最大值、最小值及其索引 #include <iostream> #include <vector> #include <algorithm> // 包含 std::max_element 和 std::min_elementint main() {std::vector<int> vec {3, 1, 4, 2, 5};// 查找最…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享

文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的&#xff0c;根据Excel列的需求预估的工时直接打骨折&#xff0c;不要问我为什么&#xff0c;主要…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...