什么是单例模式?

在我们正式讲解单例模式之前,没有了解过的小伙伴可能会有疑问...到底啥叫单例模式??其实单例模式呢,是我们设计模式中的一种,所谓的设计模式,你可以把它理解为一个模板,也就是你在实现某种业务的时候,选择适配的设计模式,根据这个模板来改你对应的业务代码

Java设计模式是解决特定软件设计问题的经典、可复用的方案模板,分为创建型、结构型和行为型三大类,帮助开发者编写更灵活、可维护的代码。

那么我们的单例模式呢,指的是实例对象只会被创建一次这样的设计模式~~

为了实现这样的要求,单例模式中又有两种形式:饿汉模式懒汉模式,接下来我们将会为大家一一介绍这两种模式

饿汉模式

什么是饿汉模式

所谓的饿汉模式,其实指的是实例从代码刚开始运行的时候就已经创建好的模式,那实例就处在一个等着被调用的状态,所以就一直饿着来等待资源~~因此就叫做饿汉模式啦

饿汉模式实现

class Singleton {   //饿汉模式private static Singleton instance = new Singleton();public static Singleton getInstance() {return instance;}private Singleton() {}}
public class Demo19 {public static void main(String[] args) {Singleton t1 = Singleton.getInstance();Singleton t2 = Singleton.getInstance();boolean res = t1 == t2;System.out.println(res);}
}

如上,我们运行结果为true,因为此时它们引用的都是同一个实例~~所以是符合单例模式的

我们可以发现,单例模式的实现,即外部无法创建一个实例是通过将构造方法变为private来实现的,此时的话外部只能通过getInstance()方法来访问内部已经创建好的那个实例

线程安全问题

那么,饿汉模式有没有线程安全问题呢?我们可以发现,饿汉模式中只有读操作,所以是没有线程安全问题的~~可以放心大胆使用

不过饿汉模式还是有缺点的,因为实例一开始就被创建了,一直等着被使用,这是很浪费资源的行为~~

懒汉模式

什么是懒汉模式

所谓的懒汉模式,指的是实例只有在被使用的时候才会开始创建,因此听起来就很懒啦,所以就被叫做懒汉模式,不过这里的"懒"可是褒义词,因为这可以节约资源~~

懒汉模式实现

class SingletonLazy {private static SingletonLazy instance = null;public static SingletonLazy getInstance() {if (instance == null) {             //判断是否有实例创建了instance = new SingletonLazy();}return instance;}private SingletonLazy() {}
}
public class Demo20 {   //懒汉模式public static void main(String[] args) {SingletonLazy t1 = SingletonLazy.getInstance();SingletonLazy t2 = SingletonLazy.getInstance();System.out.println(t1 == t2);}
}

上述我们给出了一个按照描述的懒汉模式,实例一开始是null,只有当被调用的时候才会根据判断实例是否创建过来创建实例

线程安全问题

当我们细看上述代码的时候,其实可以发现,对于instance对象,我们既有读操作,又有写操作,所以事实上,这个代码是存在线程安全问题的

当t1和t2同时调用的时候,有可能会创建两个实例,这就不符合单例模式的要求了;

我们细看可以发现,此时创建实例这个操作并不是原子的,是if + 创建一起的,这也是会引发线程安全问题的原因

因此,为了解决上述问题,我们可以把这个操作加个锁,把它们变成原子的~~

解决原子性问题代码
class SingletonLazy {private static SingletonLazy instance = null;private static Object locker = new Object();public static SingletonLazy getInstance() {synchronized (locker) {                 //使得if和创建实例变成一个整体的原子操作,防止多个线程同时操作时创建多个实例if (instance == null) {             //判断是否有实例创建了instance = new SingletonLazy();}}return instance;}private SingletonLazy() {}
}
public class Demo20 {   //懒汉模式public static void main(String[] args) {SingletonLazy t1 = SingletonLazy.getInstance();SingletonLazy t2 = SingletonLazy.getInstance();System.out.println(t1 == t2);}
}

OK~~我们的原子性问题已经解决啦(●'◡'●)

但是...我们再来看下这个代码,线程安全问题有没有解决完呢?回顾我们P4中提到的引起线程安全问题的原因,此时其实我们是两个线程在对一个对象进行操作,所以是很可能发生指令重排序和内存可见性问题的,因此我们应该给这个对象加上volatile~~

解决指令重排序和内存可见性问题
class SingletonLazy {private static volatile SingletonLazy instance = null;  //volatile防止指令重排序private static Object locker = new Object();public static SingletonLazy getInstance() {synchronized (locker) {                 //使得if和创建实例变成一个整体的原子操作,防止多个线程同时操作时创建多个实例if (instance == null) {             //判断是否有实例创建了instance = new SingletonLazy();}}return instance;}private SingletonLazy() {}
}
public class Demo20 {   //懒汉模式public static void main(String[] args) {SingletonLazy t1 = SingletonLazy.getInstance();SingletonLazy t2 = SingletonLazy.getInstance();System.out.println(t1 == t2);}
}

OK~~~这下这两个问题也解决啦(●'◡'●)

这样看起来,线程好像是没啥安全问题了勒,那有没有啥可以优化的?

经典生活例子

为了使大家更好理解我们的优化策略,在po出代码之前,我们先搞个生活化例子理解一下~~

比如说学校里面的校花突然恢复单身了,那广大单身男青年们听到这个消息之后都很开心呐,不过大家素质都很高,于是在追求校花的时候排起了队,按序追求,你很幸运的抢到了第一个,你开始追求校花之后就相当于追求校花这个操作加锁了,别人就不能进行了,校花觉得你特别好,于是答应和你在一起啦~~[成功创建了实例],但是队伍里面的人此时还不知道,第二个人此时又去追求校花,追求校花这个操作就又加锁了,这个时候她就说我有男朋友啦,第二个人出去是不是就会告诉其它人,校花有男朋友了这件事情,那么实际上,他们就不会再进行追求校花这个操作了,即甚至连竞争锁这个操作都不会去做,因为这是浪费资源的

所以与例子同样的问题,我们这个代码此时每个线程都还会再去竞争一次锁,但如果实例已经存在了,就没有竞争锁的必要了,只有一开始的几个线程在实例被创建好之前就进入创建实例过程会竞争一下锁~~~

解决重复竞争锁问题
class SingletonLazy {private static volatile SingletonLazy instance = null;  //volatile防止指令重排序private static Object locker = new Object();public static SingletonLazy getInstance() {if (instance == null) {                     //后续线程可以防止重复加锁synchronized (locker) {                 //使得if和创建实例变成一个整体的原子操作,防止多个线程同时操作时创建多个实例if (instance == null) {             //判断是否有实例创建了instance = new SingletonLazy();}}}return instance;}private SingletonLazy() {}
}
public class Demo20 {   //懒汉模式public static void main(String[] args) {SingletonLazy t1 = SingletonLazy.getInstance();SingletonLazy t2 = SingletonLazy.getInstance();System.out.println(t1 == t2);}
}

标准懒汉模式实现代码

我们这里总结一下解决完所有问题之后的代码

class SingletonLazy {private static volatile SingletonLazy instance = null;  //volatile防止指令重排序private static Object locker = new Object();public static SingletonLazy getInstance() {if (instance == null) {                     //后续线程可以防止重复加锁synchronized (locker) {                 //使得if和创建实例变成一个整体的原子操作,防止多个线程同时操作时创建多个实例if (instance == null) {             //判断是否有实例创建了instance = new SingletonLazy();}}}return instance;}private SingletonLazy() {}
}
public class Demo20 {   //懒汉模式public static void main(String[] args) {SingletonLazy t1 = SingletonLazy.getInstance();SingletonLazy t2 = SingletonLazy.getInstance();System.out.println(t1 == t2);}
}

❤❤感谢观看~~对你有帮助的话给博主点个大大的赞吧(●'◡'●)谢谢~~~❤❤

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

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

相关文章

kubernetes中数据存储etcd

etcd 在 Kubernetes 中的角色核心定位:Kubernetes 的 唯一持久化数据存储(一致性数据库)。职责: 保存整个集群的期望状态(desired state),包括节点信息、Pod 清单、Service 定义、ConfigMap、Se…

Linux crontab定时任务

参考资料 【図解】cronの仕組み定时任务 - crontab解决ubuntu下定时任务不执行问题crontab环境变量问题💥Linux定时任务功能详解:crontab与at命令应用指南 目录一. 环境准备1.1 wsl开启systemd1.2 开启cron日志二. cron服务管理相关命令2.1 service 的方…

企业频繁收到软件律师函?如何彻底解决这一难题

1. 引言:律师函频发,已成信息化管理的“隐形雷区”在工业制造、芯片、航空航天、船舶制造、医疗器械等高要求行业,软件不仅是研发与生产的关键工具,更是企业数据与知识产权安全的“底座”。然而,不少企业却在日常运营中…

在 macOS 上顺利安装 lapsolver

一、什么是 lapsolver? lapsolver 是一个用于求解线性分配问题(Linear Assignment Problem, LAP) 的 Python 库。线性分配问题是运筹学中的经典问题,核心是在两个集合(如“工人”与“任务”)之间找到一组最…

宋红康 JVM 笔记 Day02|JVM的架构模型、生命周期、发展历程

一、今日视频区间 P13-P25 二、一句话总结 JVM的架构模型;JVM的生命周期;JVM发展历程; 三、关键图/命令 3.1 JVM的架构模型Java程序对.class字节码文件进行反编译操作:在idea中先运行需要反编译的代码,找到对应的字节码…

Linux新手上路 | 在Ubuntu上Pluma文本编辑器的安装与基本使用

Linux新手上路 | 在Ubuntu上Pluma文本编辑器的安装与基本使用一、Pluma工具介绍1.1 Pluma 工具概述1.2 主要功能1.3 适用场景二、安装Pluma2.1 安装方法2.2 启动Pluma工具三、汉化方法3.1 安装汉化包3.2 设置系统语言3.3 重新打开Pluma四、基本使用方法4.1 编写文本内容4.2 关键…

React 揭秘:从新手到高手的进阶之路

目录 React:前端开发新宠​ React 初相识​ 什么是 React​ React 的核心特性​ 1.组件化开发 2.虚拟 DOM 与 Diff 算法 单向数据流 搭建 React 开发环境 环境准备​ 创建 React 项目 项目结构解析 React 基础语法与核心概念 JSX 语法​ 基本语法规则…

八股文小记 Servlet 过滤器-Spring MVC 拦截器-Spring AOP 拦截器区别

您对执行机制的洞察非常准确!让我们深入分析这三种组件的调用机制及其与 AOP 节点的关系: 一、执行机制的本质区别组件调用机制实现原理Servlet 过滤器递归调用通过 FilterChain.doFilter() 显式递归调用下一个节点Spring MVC 拦截器遍历调用由 HandlerE…

qml 实现数值键盘

import QtQuick 2.0import QtQuick.Layouts 1.12 import"../pad" // PasswordKeyboard.qml import QtQuick 2.12ColumnLayout {id: keyboardspacing: 8// 键盘标题Text {text: "安全输入"font.pixelSize: 16color: "#666"Layout.alignment: Qt.A…

PID控制算法

文章目录引言一、基本原理1.1.简介1.2.开环与闭环1.3.PID 的公式1.3.1.比例项(Proportional)1.3.2.积分项(Integral)1.3.3.微分项(Differential)1.4.连续形式与离散形式的 PID 公式1.4.1.连续形式1.4.2.离散…

MyBatis 动态数据源切换在 Spring Boot 环境下的实现方案

第一章 需求背景与技术选型1.1 多数据源场景概述在大型企业级应用中,单一数据库往往无法满足高并发和多业务线的需求,因此需要引入 多数据源 的架构设计。常见的多数据源场景包括:读写分离、多租户、分库分表以及数据源负载均衡等。读写分离&…

PCA降维理论详解

文章目录一、什么是PCA?二、为什么需要降维?三、PCA的数学原理与详细推导视角一:最大化投影方差(Maximizing Variance)视角二:最小化重构误差(Minimizing Reconstruction Error)四、…

Android RxJava变换操作符详解

RxJava作为响应式编程在Android开发中的利器,其强大的变换操作符能够帮助我们优雅地处理数据流。本文将深入讲解RxJava中最常用的变换操作符及其实际应用场景。一、RxJava变换操作符概述变换操作符(Transformation Operators)用于对Observable发射的数据序列进行变换…

开源数据发现平台:Amundsen 快速上手指南

Amundsen 是一个数据发现和元数据引擎,旨在提高数据分析师、数据科学家和工程师与数据交互时的生产力。目前,它通过索引数据资源(表格、仪表板、数据流等)并基于使用模式(例如,查询频率高的表格会优先于查询…

【密码学实战】国密SM2算法介绍及加解密/签名代码实现示例

引言 在信息安全领域,密码算法是数据保护的核心基石。2010 年,中国国家密码管理局发布了 SM2 椭圆曲线公钥密码算法,作为国产密码标准的核心成员,它凭借高效安全的特性,逐步替代 RSA 等国际算法,广泛应用于…

QT开发中如何加载第三方dll文件

文章目录🔧 一、隐式加载(静态链接)操作步骤:⚙️ 二、显式加载(动态链接,推荐使用QLibrary)操作步骤:💻 三、直接调用Windows API(仅Windows)⚠️…

后端学习资料 持续更新中

数据库: 该网址包含:图解MySql, 看明白谁也问不倒你~ 图解计算机网络、操作系统、计算机组成、MySQL、Redis,让天下没有难懂的八股文!https://xiaolincoding.com/

《嵌入式Linux应用编程(六):并发编程基础:多进程exec函数族及多线程基础》

一、exec函数族在一个进程里面执行另一个文件本质&#xff1a;将文本区的指令代码替换成exec要执行的指令#include <unistd.h>参数&#xff1a;path:要执行的可执行文件的路径和名称arg:执行该可执行文件时需要传递的参数NULL&#xff1a;参数传递结束标志 返回值&#x…

【121页PPT】智慧方案智慧综合体智能化设计方案(附下载方式)

篇幅所限&#xff0c;本文只提供部分资料内容&#xff0c;完整资料请看下面链接 https://download.csdn.net/download/2501_92808859/91654007 资料解读&#xff1a;【121页PPT】智慧方案智慧综合体智能化设计方案 详细资料请看本解读文章的最后内容 一、项目概述与智能化总…

Linux网络基础(一)

目录 计算机网络背景 网络发展 初识 "协议" 网络协议初识 协议分层 软件分层的好处 打电话例子 OSI七层模型 TCP/IP五层(或四层)模型 参考资料 再识协议 为什么要有 TCP/IP 协议&#xff1f; 什么是 TCP/IP 协议&#xff1f; TCP/IP 协议与操作系统的关系(宏观上&…