【JavaEE】单例模式详解

news2025/1/19 8:21:25

目录

一、单例模式的概念

二、饿汉模式

三、懒汉模式

(1)懒汉模式-单线程版

(2)懒汉模式-线程安全多线程版


        啥是设计模式咧🤔设计模式好比象棋中的棋谱。红方当头炮,黑方马来跳👣。针对红方的一些走法,黑方应招的时候有一些固定套路。

        软件开发中也有很多常见的 "问题场景"。针对这些问题场景,,大佬们总结出了一些固定的套路.。按照这个套路来实现代码,就是设计模式。常见的设计模式有23种。

总体来说设计模式分为三大类:

创建型模式:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

        本文主要讲述最简单的单例模式。

一、单例模式的概念

        单例模式是设计模式中最简单的形式之一。单例模式能保证某个类在程序中只存在唯一一份实例, 而不会创建出多个实例。

        单例模式的三个要点:1.某个类只能有一个实例。2.自行创建这个实例。3.自行向整个系统提供这个实例。具体实现方式:1.单例模式的类只提供私有的构造函数。2.类定义中含有一个该类的静态私有对象。3.该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。

        单例模式的应用场景很多。例如:JDBC 中的 DataSource 实例就只需要一个、一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个文件管理器或者窗口管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。单例模式具体的实现方式, 分成 "饿汉" 和 "懒汉" 两种。

二、饿汉模式

        类加载的同时, 创建实例。饿汉先把食物准备好,当饿了的啥时候就吃饭。

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

饿汉模式注意点:

1.构造方法私有化

2.private static Singleton instance = new Singleton();中static表示类方法,只能访问类成员、类变量。创建一个实例,进行初始化,整个程序,只有这么一个实例。

3.public static Singleton getInstance() 中使用static修饰的方式,也是类方法。调用的话使用Singleton.getInstance。

三、懒汉模式

        类加载的时候不创建实例,第一次使用的时候才创建实例。懒汉当饿了的时候,才想着准备食物。

        例如:手机淘宝,在用户打开手机淘宝时,只加载第一页的内容。当用户往下拉,再继续加载后面的内容。好处就是加载会非常快。文本文件同理,linux一些命令,比如cat命令就是使用懒加载,看一些加载一页,第一次打开时就非常快。

        懒汉模式步骤:

1.现将构造方法改为私有

2.创建一个实例但不对其进行初始化

3.对外提供一个方法来获取这个实例并对其进行初始化

(1)懒汉模式-单线程版

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

(2)懒汉模式-线程安全多线程版

        如果按照上述代码,那么会存在线程不安全的问题。线程安全问题发生在首次创建实例时。如果在多个线程中同时调用 getInstance 方法, 就可能导致创建出多个实例。
        我们需要做的是一旦实例已经创建好了,后面再多线程环境调用 getInstance 就不再有线程安全问题了,不再修改instance实例了。

        可以通过同步synchronized 改善线程安全问题。

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

在加锁的情况下进一步改进:

使用双重 if 判定, 降低锁竞争的频率.
给 instance 加上了 volatile.

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

 小贴士:

        外层的 if (instance == null)是判断是否枷锁。当instance!=null的时候,两个线程同时访问,就不需要进行锁的竞争;内层的 if (instance == null)是避免重复实例化,解决线程安全问题。两个if判断互相不能代替。

        加锁 /解锁是一件开销比较高的事情。对于懒汉模式的线程不安全只发生在首次创建实例的时候。因此后续使用的时候, 就不必再进行加锁了。外层的 if 就是判定当前是否已经把 instance 实例创建出来。

        同时为了避免 "内存可见性" 导致读取的 instance 出现偏差,,于是补充上 volatile 。当多线程首次调用 getInstance, 大家可能都发现 instance 为 null, 于是又继续往下执行来竞争锁,其中竞争成功的线程, 再完成创建实例的操作。当这个实例创建完了之后, 其他竞争到锁的线程就被里层 if 挡住了。也就不会继续创建其他实例。


1) 有三个线程,开始执行 getInstance ,通过外层的 if (instance == null) 知道了实例还没有创建的消息。于是开始竞争同一把锁。

2) 其中线程1 率先获取到锁,此时线程1 通过里层的 if (instance == null) 进一步确认实例是否已经创建. 如果没创建, 就把这个实例创建出来。

3) 当线程1 释放锁之后,线程2 和 线程3 也拿到锁,也通过里层的 if (instance == null) 来确认实例是否已经创建,发现实例已经创建出来了,就不再创建了。

4) 后续的线程不必加锁,直接就通过外层 if (instance == null) 就知道实例已经创建了,从而不再尝试获取锁了,降低了开销。

懒汉模式不可缺少的四部分:构造函数私有化、正确加锁、双重check、volatile


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

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

相关文章

鸿蒙开发学习|HarmonyOS是什么

鸿蒙开发学习 第一章 HarmonyOS是什么文章目录鸿蒙开发学习前言一、什么是HarmonyOS二、HarmonyOS系统架构2.1 内核层2.2系统服务层2.3框架层2.4应用层三、HarmonyOS系统特性3.1 对消费者3.2.对应用开发3.3.对设备开发四、HarmonyOS系统四大技术特性4.1 分布式架构首次用于终端…

LabVIEW最大内存块属性不存在

LabVIEW最大内存块属性不存在在NI Linux实时操作系统目标中使用系统属性节点和分布式系统管理器(DSM),但遇到一些问题:它未正确报告系统上的可用物理内存量。在NI Linux实时系统上出现错误-2147220623尝试在NI Linux实时上监测RAM…

webpack自动化打包

webpack自动化打包 首先下载包 npm i webpack-dev-server -D 配置 webpack.config.js const path require(path)//nodejs核心模块,专门用来处理路径问题 const ESLintPlugin require(eslint-webpack-plugin) const HtmlWebpackPlugin require(html-webpack-p…

java-加密、解密算法

rsa2048、sha256 rsa2048对整个文件进行hash算法,生成公钥、私钥后用于数字签名, sha256通过公钥和私钥,作为证书使用。单板打包后对每个动态库sha256计算颁发证书 sha256签名后,rsa2048进行加密。签名用于校验加密数据没有被更…

初始化一个vite+vue3项目,配置eslint+Prettier

引用vite官网的一段话,解释一下我们为什么要选择使用vite 时过境迁,我们见证了诸如 webpack、Rollup 和 Parcel 等工具的变迁,它们极大地改善了前端开发者的开发体验。 然而,当我们开始构建越来越大型的应用时,需要处…

Java后端数据校验学习总结

Java后端校验总结 后端校验注解一直在用,但是感觉不是特别清楚,希望通过写这篇文章搞清楚。 Spring自带的Validation校验框架 Spring提供了Validator接口来校验对象,主要涉及到的方法和类如下: supports方法:设置校…

win11任务栏图标闪烁|任务栏QQ图标闪动|新消息任务栏自动弹出|设置自动隐藏任务栏之后,QQ或微信等工具新消息自动弹出任务栏并颜色提示问题解决方案

背景介绍: 今天正常使用电脑时也出现消息弹出问题(已经设置隐藏任务栏),很头疼那么时什么情况,该如何组去解决呢?(微信任务栏闪动未读消息) MyDockFinder Windows 桌面美化工具 目录 背景介绍 解决问题 微信环境测试 初始界面(微信) 打开微信 …

PostMan简介2022黑马跟学

2022黑马PostMan简介跟学1.PostMan工具的使用1.1 PostMan简介1.2 PostMan安装1.3 PostMan使用1.3.1 创建WorkSpace工作空间1.3.2 发送请求(1).以百度为例发送get请求(2).以百度为例发送post请求1.3.3 保存当前请求1.3.4 创建server端1.PostMan工具的使用 1.1 PostMan简介 代码…

运算放大电路(三)-加法器

加法器 由虚短知: V- V 0 ……a 由虚断及基尔霍夫定律知,通过R2与R1的电流之和等于通过R3的电流,故 (V1 – V-)/R1 (V2 – V-)/R2 (Vout – V-)/R3 ……b 代入a式,b式变为 V1/R1 V2/R2 Vout/R3 如果取 R1R2R3 则上式变为 Vo…

ideal整合reids实现缓存查询

目录 前言: 一.工作流程 二. RedisConfig类 三.application.properties 四.开启linux中的redis 五.使用redis结合数据库进行数据查询 5.1编程式缓存 5.2声明式缓存 5.2.1在启动类上添加注解 5.2.2实现代码 六.运行结果 ​编辑 前言: 废话不多讲…

Java文件读写和CSV文件解析(读取csv文件的一列或若干列)

文件类 Java 读文件流的知识不可少,先复习一下吧! OREACLE JDK8 DOCS 文件类是Java IO的一个对象,用于指定文件的相关信息,位置和名称信息。如txt文件,csv文件对Java来说就是一个文件类。 开发手册中指出&#xff0…

Android实战场景 - 保存WebView中的图片到相册

去年同事写了一个 “在H5中保存图片到相册” 的功能,虽然有大致实现思路,实现起来也没问题,但是感觉同事考虑问题的很周全,当时候就想着去学习一下,但是项目太赶没顾得上,索性现在有时间,准备好…

2023-02-01 读书笔记:《有趣的统计》-1-基础知识

2023-02-01 读书笔记:《有趣的统计》-1-基础知识 75招学会数据分析 —— 2014 Doctor.Bruce Frey 序 统计学: 最初,用于确定某些事情发生的可能性;不断发展,根据样本数据准确推断总体数据特征的方法(推…

c语言基础之分支和循环语句

c语言基础之分支和循环语句分支语句和循环语句什么是语句?if语句switch语句在switch语句中的 breakwhile循环while语句中的break和continuefor循环break和continue在for循环中do...while()循环goto语句写在最后:📌————本章重点————&a…

全球疫情期间的校园招聘:可以学到的6个教训

疫情放开后,校园招聘逐渐回暖,谁能率先有效整合线上线下校招,企业将从一开始就处于战略领先地位。下面梳理了全球招聘团队在疫情期间的6个校招教训,希望对你有启发。01重新规划线上工具的应用玩法现如今,学生已通过网课…

【C++提高编程】vector容器详解(附测试用例与结果图)

目录1.vector容器1.1 vector基本概念1.2 vector构造函数1.3 vector赋值操作1.4 vector容量和大小1.5 vector插入和删除1.6 vector数据存取1.7 vector互换容器1.8 vector预留空间1.vector容器 1.1 vector基本概念 功能: vector数据结构和数组非常相似,…

【C++】类和对象(上)

文章目录1. 面向过程和面向对象的初步认识2. 类的引入3. 类的定义4. 类的访问限定符及封装4.1 访问限定符4.2 封装5. 类的作用域6. 类的实例化7. 类对象模型7.1 如何计算对象的大小7.2 类对象的存储方式7.3 结构体内存对齐规则8. this指针8.1 this指针的引出8.2 this指针的特性…

性能技术分享|Jmeter+InfluxDB+Grafana搭建性能平台(二)

二、CentOS安装:方式一:把下载的.rpm包推送到服务器上;方式二:直接命令行安装#下载wget https://dl.influxdata.com/influxdb/releases/influxdb-1.7.1.x86_64.rpm#安装yum localinstall?influxdb-1.7.1.x86_64.rpm -y2.3 修改配…

三十一、RabbitMQ(2)

🌻🌻 目录一、RabbitMQ 入门及安装1.1 概述1.3 Erlang 安装1.2.1 安装下载1.2.3 安装依赖环境1.2.4 安装 Erlang1.2.4 Erlang安装成功1.3 安装 RabbitMQ1.5启动 rabbitmq 服务1.4 开启管理界面及配置1.5.1 设置配置文件二、RabbitMQWeb 管理界面及授权操…

【JVM】垃圾回收算法与分代回收

文章目录1. 垃圾回收算法概述2. 标记-清除算法3. 标记-复制算法4. 标记-整理算法5. 分代回收本文参考:深入理解Java虚拟机:JVM高级特性与最佳实践(第3版) 1. 垃圾回收算法概述 根据判定对象消亡的角度来看,垃圾收集算…