JavaEE 初阶篇-深入了解单例模式(经典单例模式:饿汉模式、懒汉模式)

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍

文章目录

        1.0 单例模式的概述

        2.0 单例模式 - 饿汉式单例

        2.1 关于饿汉式单例的线程安全问题

        3.0 单例模式 - 懒汉式单例

        3.1 关于懒汉式单例的线程安全问题

        3.1.1 加锁 synchronized 方法

        3.1.2 双重检查锁定


        1.0 单例模式的概述

        单例模式是一种常见的设计模式,其核心思想是确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。单例模式通常用于需要全局访问点且只需要一个实例的情况。

单例特点包括:

        1)一个类只有一个实例对象。

        2)提供全局访问点,允许其对象访问这个实例。

        3)通过静态方法或静态变量实现。

常见的单例模式实现方法包括:

        1)饿汉式单例:在类加载时即创建实例。

        2)懒汉式单例:在第一次使用时创建实例。

使用单例模式的好处:

        使用单例模式可以避免多次实例化对象,节省内存资源,并且提供一个统一的访问点,方便管理和维护。

        2.0 单例模式 - 饿汉式单例

        “饿汉”顾名思义,有迫不及待的意思,看到食物就迫切想吃。因此,在类加载的时候,需要立即创建实例对象。为了保证只有一个实例对象,对外的静态对象实例需要封装起来,且对外的构造器也需要封装起来,对外有一个获取实例对象的静态方法即可。

代码如下:

public class Singleton {// 在类加载时即创建实例对象private static Singleton instance = new Singleton();// 将构造器设为私有,外部无法直接实例化private Singleton() {}// 提供一个公共的静态方法来获取实例对象public static Singleton getInstance() {return instance;}// 其他方法和属性
}

        通过这种方式,可以保证在程序运行过程中始终只有一个实例对象存在,且外部通过静态方法 getInstance() 来获取实例对象,确保了单例模式的实现。这种方式适合在程序初始化时就需要创建实例对象的情况下使用。

        2.1 关于饿汉式单例的线程安全问题

        在“饿汉式”单例模式中,由于在类加载时就创建实例对象,并且在静态变量中直接初始化实例,因此在多线程环境下是线程安全的。

        类加载过程是由类加载器负责,保证了在多线程环境下只会加载一次类,因此也只会创建一个实例对象。

        因此,如果使用“饿汉式”单例模式,在多线程环境下不需要额外的同步措施,可以保证线程安全。每个线程在访问该单例类的时候都会得到同一个实例对象,不会出现多个实例对象被创建的情况。

        3.0 单例模式 - 懒汉式单例

        当需要获取第一个实例对象的时候,才会去创建实例对象。

        为了保证该类只有一个实例对象,需要对外的构造方法封装,在还没获取实例对象的时候,将静态变量赋值为 null 。再提供一个静态方法来获取实例对象,当获取实例对象的时候,先会判断还对象的变量是否为 null ,如果为 null 时,这时候需要立即创建对象,然后返回该对象;如果实例对象的变量不为 null 时,直接方法该实例对象。

代码如下:

public class SingletonLazy {private static SingletonLazy singleton = null;//私有的构造方法private SingletonLazy(){}//获取实例对象的方法,变量不为 null 的时候直接返回该实例对象public static SingletonLazy getInstance(){if (singleton == null){singleton = new SingletonLazy();}return singleton;}//其他方法和属性}

        3.1 关于懒汉式单例的线程安全问题

        懒汉式单例模式在多线程环境下存在安全问题,需要通过加锁等方式来解决。

        在懒汉单例模式中,实例对象在第一次被调用时才会被创建,如果这时候,多个线程同时调用 getInstance() 方法,可能会导致多个实例对象被创建,破环了单例的特性。

解决方法:可以通过加锁方式来确保线程安全

        3.1.1 加锁 synchronized 方法

        在 getInstance() 方法上加上 synchronized 关键字,确保在多线程环境下只有一个线程可以进入该方法,保证只创建一个实例对象。但是这种方式会影响性能,因为每次调用 getInstance() 都需要获取锁。

代码如下:

public class SingletonLazy {private static SingletonLazy singleton = null;//私有的构造方法private SingletonLazy(){}//获取实例对象的方法,变量不为 null 的时候直接返回该实例对象public static synchronized SingletonLazy getInstance(){if (singleton == null){singleton = new SingletonLazy();}return singleton;}//其他方法public void fun(){System.out.println("懒汉单例模式");}
}

        在静态方法 getInstance() 方法上加上了 synchronized 修饰。每当多线程抢占获取锁的时候,没有获取锁的线程需要阻塞等待。这就保证了线程安全问题。

        3.1.2 双重检查锁定

        从代码执行效率这方面来看,每一个获取实例对象的时候,都需要阻塞等待,且只有创建对象的时候才需要真正的用到锁这个作用,其余大部分不需要来创建实例对象,只需要获取实例对象。

        在 getInstance() 方法中进行双重检查,先判断实例是否已经被创建,如果没有再进行加锁创建实例。这种方式可以减少加锁的次数,提高性能。

优化代码如下:

public class SingletonLazy {private static volatile SingletonLazy singleton = null;//私有的构造方法private SingletonLazy(){}//获取实例对象的方法,变量不为 null 的时候直接返回该实例对象public static SingletonLazy getInstance(){if (singleton == null){synchronized (SingletonLazy.class){if (singleton == null){singleton = new SingletonLazy();}}}return singleton;}//其他方法public void fun(){System.out.println("懒汉单例模式");}
}

        第一个 if 判断是否要加锁,实例化之后,线程自然安全,就无需加锁了,实例化之前, new 之前,就应该加锁。

        第二个 if 判断是否要创建对象。

        通过双重 if 避免了不可重复读带来的负面影响,避免了重复创建对象。

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

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

相关文章

spring boot3登录开发-3(2短信验证登录/注册逻辑实现)

⛰️个人主页: 蒾酒 🔥系列专栏:《spring boot实战》 🌊山高路远,行路漫漫,终有归途 目录 写在前面 上文衔接 内容简介 功能分析 短信验证登录实现 1.创建交互对象 用户短信登录/注册DTO 创建用户登录VO…

IOTX:未来市场爆发点的RWA协议?DePIN赛道被低估的龙头

从基本面来看,IoTeX的目标是创建一个连接的世界,在这个世界中,每个人都能控制自己的数据、设备和身份。通过区块链技术,IoTeX旨在解锁智能设备和数据的潜力,支持新一代的现实世界Dapp和数字资产的发展。IOTX始终致力于…

红黑树剖析

目录 一. 红黑树的概念 二. 红黑树的性质 三. 红黑树节点的定义 四. 红黑树的插入操作 4.1 uncle存在且颜色为红 4.2 uncle不存在或者uncle存在且为黑 五. 整体代码展示 一. 红黑树的概念 红黑树,是一种二叉搜索树,但在每…

OpenCV 4.9基本绘图

返回:OpenCV系列文章目录(持续更新中......) 上一篇:OpenCV使用通用内部函数对代码进行矢量化 下一篇:OpenCV系列文章目录(持续更新中......) ​目标 在本教程中,您将学习如何&am…

ResutBuilder 学习笔记二:增加新的输入数据类型

ResutBuilder 学习笔记二:增加新的输入数据类型 我们在前面的博客中创建了一个非常简单的结果构建器ConcatBuilder,用于连接多个字符串。 ConcatBuilder虽然非常简单,但已经展现出一些令人兴奋的特征,代码简洁,清晰&a…

广东小团队惊艳业绩揭秘:链动模式助力面膜销售破千万!

惊爆!广东一个默默无闻的小团队竟然在短短一个月内,仅凭销售面膜就实现了超过千万的惊人业绩!这背后究竟隐藏着怎样的秘密武器呢? 揭开链动模式的神秘面纱 链动模式,作为社交电商领域的一股新兴力量,正以其…

[Python学习篇] Python注释

注释 单行注释 # # 单行注释 多行注释 """ """ 多行注释1 多行注释2 """ 多行注释 多行注释1 多行注释2

【强化学习的数学原理-赵世钰】课程笔记(一)基本概念

目录 一. 内容概述1. 通过案例介绍强化学习中的基本概念2. 在马尔可夫决策过程(MDP)的框架下将概念正式描述出来 二. 通过案例介绍强化学习中的基本概念1. 网格世界(A grid world example)2. 状态(State)3.…

Spring AOP + 自定义注解 实现公共字段的填充

Spring AOP 自定义注解 实现公共字段的填充 代码冗,不利于后期维护. 定义操作这些字段的方法类型 实现步骤: 自定义注解AutoFill,用于表示操作这些公共字段的方法自定义切面类AutoFillAspect,统一拦截,通过反射获取方法入参,并填充公共字段…

mysql常见故障及mysql优化

目录 mysql单实例常见故障 mysql主从常见故障 mysql优化 硬件优化 查询优化 操作系统的优化 my.cnf内参数的优化 mysql单实例常见故障 ERROR 2002 (HY000): Cant connect to local MySQL server through scoket /data/mysql/mysql.sock问题分析 数据库未启动或者数据库…

【THM】Burp Suite:Other Modules(其他模块)-初级渗透测试

介绍 除了广泛认可的Repeater和Intruder房间之外,Burp Suite 还包含几个鲜为人知的模块。这些将成为这个房间探索的重点。 重点将放在解码器、比较器、排序器和组织器工具上。它们促进了编码文本的操作,支持数据集的比较,允许分析捕获的令牌内的随机性,并帮助您存储和注释…

9、鸿蒙学习-开发及引用静态共享包(API 9)

HAR(Harmony Archive)是静态共享包,可以包含代码、C库、资源和配置文件。通过HAR可以实现多个模块或多个工程共享ArkUI组件、资源等相关代码。HAR不同于HAP,不能独立安装运行在设备上,只能作为应用模块的依赖项被引用。…

MongoDB 6.1 及以上版本使用配置文件的方式启动报错 Unrecognized option: storage.journal.enabled

如果你使用的 MongoDB 的版本大于等于 6.1,并且在 MongoDB 的配置文件中编写了如下内容 storage:journal:# 启用或禁用持久性日志以确保数据文件保持有效和可恢复# true 启用;false 不启用# 64 位系统默认启用,启用后 MongoDB 可以在宕机后根…

Linux多进程通信(1)——无名管道及有名管道使用例程

管道是半双工通信,如果需要 双向通信,则需要建立两个管道, 无名管道:只能父子进程间通信,且是非永久性管道通信结构,当它访问的进程全部终止时,管道也随之被撤销 有名管道:进程间不需…

RK3568驱动指南|第十四篇 单总线-第162章DS18B20驱动读时序编写

瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工…

秒验资深玩家熬夜整理的15个常见问题,拿走不谢!

1、双卡双待手机如何登录? 针对双卡双待手机只取当前流量卡号 2、用其他手机号如何登录? 使用传统验证方式登录,例如验证码登录 3、一键登录只支持4G吗? 电信支持4G,5G网络取号,移动, 联通支持5G,4G, 3G, 2G网络取号…

数值金额计算js封装包含加减乘除四个方法,能确保浮点数运算不丢失精度

项目场景: 商城类项目中大多需要金额计算,我们知道计算机编程语言里浮点数计算会存在精度丢失问题(或称舍入误差),其根本原因是二进制和实现位数限制有些数无法有限表示 以下是十进制小数对应的二进制表示&#xff1…

python笔记(8)Tuple(元组)

目录 创建元组 元组取数 不支持修改和删除元素 元组运算符 元组内置函数 创建元组 Tuple 元组的元素不能修改,元组使用小括号(),创建元组在括号里添加元素用逗号隔开即可。 创建空元组 tup1() 创建一个元素的元组&#xff…

Golang 开发实战day06 - Boolean Conditional

Golang 教程06 - Boolean & Conditional 1. Boolean & Conditional 1.1 什么是布尔类型? 想象一下,你正在玩一个古老的游戏,只有两个选项:是或否。在 Golang 中,这就是布尔类型,用 bool 关键字表…

【Linux实验室】DNS域名解析服务——超详细实验操作!

DNS域名解析 DNS域名解析服务——超详细实验操作!!!序言DNS 基本概述分布式、层次数据库DNS 层次结构DNS 查询步骤DNS 查询类型DNS服务器类型DNS 缓存反向 DNS 查询如何检查 DNS 记录是否生效 Bind解析服务Bind简介bind的服务类型 DNS域名解析…