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

单例模式,饿汉与懒汉

文章目录

  • 什么是单例模式
  • 单例模式的两种形式
    • 饿汉模式
    • 懒汉模式
  • 懒汉模式与饿汉模式是否线程安全
  • 懒汉模式的优化

什么是单例模式

单例模式其实就是一种设计模式,跟象棋的棋谱一样,给出一些固定的套路帮助你更好的完成代码。设计模式有很多种,单例模式是在校招当中最爱考的设计模式之一。

单例就指的是单个实例,一个程序如果频繁使用一个对象且作用相同,为了防止多次实例化对象,我们就可以使用单例模式,让类只能创建出一个实例,也就是一个对象,减少开销。
有一些场景本身就是要求某一个概念是单例的,例如JDBC里的DateSores

单例模式的两种形式

在Java中实现单例模式有很多种写法,我们这里重点讲解两种,懒汉模式与饿汉模式。

饿汉模式

饿汉模式,顾名思义,当人非常饿的时候,看见了食物,那种心情是怎么样的迫不及待。饿汉模式非常着急在类进行创建时就已经迫不及待的实例化单例对象了

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

根据我们的描述可以写出这样的代码,但是我们发现,单例模式的初心我们并没有达到,单例模式的初心是让类只能实例化一次,此时我们并没有完成需求。我们通过私有化构造方法的方式来防止类的多次实例化:

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

此时我们单例模式中的饿汉模式就已经完成了,我们可以来测试一下:
在这里插入图片描述

懒汉模式

懒汉,所表示的含义并不是我们理解的流浪汉,相反懒表示的是一种从容不迫,是不着急,这种模式与饿汉模式的迫不及待不同,他只有在真正需要使用对象时才实例化单例对象。懒汉模式同样使用私有化构造方法的形式来完成初心,我们来写一下代码:

class SingletonLazy {private static SingletonLazy singletonLazy = null;public static SingletonLazy getInstance() {if(singletonLazy == null) {singletonLazy = new SingletonLazy();}return singletonLazy;}private SingletonLazy () {}
}

懒汉模式与饿汉模式是否线程安全

上面我们完成了懒汉模式与饿汉模式的代码编写,现在我们需要考虑一个问题,上面两个代码,是否线程安全,在多线程下调用getInstance()是否会出现问题。

首先我们来看饿汉模式
在这里插入图片描述
饿汉模式的getInstance()方法为只读操作,所以在多线程下调用不会有什么问题,是安全的。

懒汉模式:
在这里插入图片描述
懒汉模式在多线程下,无法保证创建对象的唯一性。

例如两个线程同时调用getInstance()方法,代码的执行顺序可能为:
1、线程一进行判断操作
2、线程二进行判断操作
3、线程一实例化对象
4、线程二实例化对象
这样线程一和线程二都会实例化对象,如果是N个线程可能会实例化N个对象,所以懒汉模式在多线程模式下不安全。

懒汉模式的优化

我们需要对懒汉模式进行优化,使得他在多线程下变得安全,如何操作呢?上面的分析中我们提到了,懒汉模式不安全的原因是,判断操作和new操作没有原子性,那么我们让他具有原子性不就可以了。我们就可以通过加锁来完成需求:

class SingletonLazy {private static SingletonLazy singletonLazy = null;public static SingletonLazy getInstance() {synchronized (SingletonLazy.class) {if(singletonLazy == null) {singletonLazy = new SingletonLazy();}}return singletonLazy;}private SingletonLazy () {}
}

这样就会有锁竞争,不会在出现向刚才那样两个线程同时进行判断的操作,一定是等一个线程new了之后,另一个线程才能竞争到锁进行判断。
我们觉得这样还是不够,不够高效,这样写虽然可以解决安全问题,但是同时也造成了效率的降低,每个线程都需要阻塞等待,但是我们分析一下,只有singletonLazy == null时才需要进行阻塞,当singletonLazy != null时其实就只是单纯的读操作。所以我们在进行优化:

class SingletonLazy {private static SingletonLazy singletonLazy = null;public static SingletonLazy getInstance() {if (singletonLazy == null) {synchronized (SingletonLazy.class) {if(singletonLazy == null) {singletonLazy = new SingletonLazy();}}}return singletonLazy;}private SingletonLazy () {}
}

这样又解决了我们的问题,上面代码中的两个判断条件看着是一样的,但是初心不一样,第一个是为了提高效率,判断是否需要加锁,第二个是为了判断是否需要实例化对象,两行代码看着离这不远,但是中间有一个加锁的操作,执行的时机其实差别很大。
这样就完了?并没有这里还有一个问题:指令重排序

什么是指令重排序呢?
创建一个对象,在jvm中会经过三步
1、创建内存空间
2、调用构造方法
3、将引用指向分配好的内存空间
我们发现,第二步和第三步好像可以进行交换执行顺序,交换之后对结果并没有影响,而这样不影响结果的情况下,可以不按照程序编码的顺序执行语句,提高程序性能的操作,我们称为指令重排序

这里我们也实力化对象了,所以也可能有指令重排序的操作,例如线程一此时new对象的时候,发生了指令重排序,在没有调用构造方法的情况下进行了分配内存空间,此时系统调度到了线程二,线程二进行判断,此时引用非空就返回,这样我们返回了一个没有调用过构造方法的引用。
如何解决问题呢?我们使用volatile就可以防止指令重排序:

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

这样懒汉模式的优化,我们就完成了。

相关文章:

单例模式,饿汉与懒汉

文章目录什么是单例模式单例模式的两种形式饿汉模式懒汉模式懒汉模式与饿汉模式是否线程安全懒汉模式的优化什么是单例模式 单例模式其实就是一种设计模式,跟象棋的棋谱一样,给出一些固定的套路帮助你更好的完成代码。设计模式有很多种,单例…...

Prometheus监控实战之Blackbox_exporter黑盒监测

1 Blackbox_exporter应用场景 blackbox_exporter是Prometheus官方提供的exporter之一,可以提供HTTP、HTTPS、DNS、TCP以及ICMP的方式对网络进行探测。 1.1 HTTP 测试 定义 Request Header信息 判断 Http status / Http Respones Header / Http Body内容 1.2 TC…...

【蓝桥杯集训·每日一题】AcWing 1051. 最大的和

文章目录一、题目1、原题链接2、题目描述二、解题报告1、思路分析2、时间复杂度3、代码详解三、知识风暴线性DP一、题目 1、原题链接 1051. 最大的和 2、题目描述 对于给定的整数序列 A{a1,a2,…,an},找出两个不重合连续子段,使得两子段中所有数字的和最…...

【Unity工具,简单应用】Photon + PUN 2,做一个简单多人在线聊天室

【Unity工具,简单应用】Photon PUN 2,做一个简单多人聊天室前置知识,安装,及简单UI大厅聊天室简单同步较复杂同步自定义同步最终效果前置知识,安装,及简单UI 【Unity工具,简单学习】PUN 2&…...

程序员增加收入实战 让小伙伴们都加个鸡腿

文章目录前言1️⃣一、发外包平台💁🏻‍♂️二、朋友介绍✍️三、打造自己的个人IP👋🏿四、混群拉单🤳🏿五、面试拉单💻六、技术顾问🦴七、开发个人项目总结:前言 程序员…...

GPIO四种输入和四种输出模式

GPIO的结构图如下所示: 最右端为I/O引脚,左端的器件位于芯片内部。I/O引脚并联了两个用于保护的二极管。 输入模式 从I/O引脚进来就遇到了两个开关和电阻,与VDD相连的为上拉电阻,与VSS相连的为下拉电阻。再连接到TTL施密特触发…...

ChatGPT能够改变时代吗?一点点思考

都知道ChatGPT的出现对整个世界产生了剧烈的影响,前不久出的ChatGPT4更是在ChatGPT3.5的基础上展现了更强的功能。比如说同一个问题,ChatGPT3.5还是乱答的,ChatGPT4已经能给出正确解了。当然这只能说明技术是进步的。 虽然如此,很…...

Markdown如何使用详细教程

目录 一、Markdown 标题 二、Markdown 段落 三、Markdown 字体 四、Markdown 分隔线 五、Markdown 列表 六、Markdown 引用 七、Markdown 代码 八、Markdown 链接 九、Markdown 图片 十、Markdown 表格 前言 当前许多网站都广泛使用 Markdown 来撰写博客,…...

HTML5庆祝生日蛋糕烟花特效

HTML5庆祝生日蛋糕烟花特效 <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>HTML5 Birthday Cake Fireworks</title><style>canvas {position: absolute;top: 0;left: 0;z-index: -1;}</style> </h…...

算法套路四——反转链表

算法套路四——反转链表 算法示例一&#xff1a;LeetCode206. 反转链表 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 初始化pre为空&#xff0c;cur为头指针 pre指针&#xff1a;记录当前结点的前一个结点 cur指针&#xff1a;记录当…...

多线程 (六) wait和notify

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点!人生格言&#xff1a;当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友一起加油喔&#x1f9be;&am…...

React--》状态管理工具—Mobx的讲解与使用

目录 Mobx的讲解与使用 Mobx环境配置 Mobx的基本使用 Mobx计算属性的使用 Mobx监听属性的使用 Mobx处理异步的使用 Mobx的模块化 Mobx的讲解与使用 Mobx是一个可以和React良好配合的集中状态管理工具&#xff0c;mobx和react的关系相当于vuex和vue之间的关系&#xff0…...

有效的括号长按键入验证外星语词典字符的最短距离用栈实现队列

有效的括号来源&#xff1a;杭哥20. 有效的括号 - 力扣&#xff08;LeetCode&#xff09;bool isValid(char * s) {int szstrlen(s);char stack[sz];int k0;for (int i0;i<sz;i){if (s[i]( || s[i][ || s[i]{){stack[k]s[i];}else{if (k0){return false;}else if (s[i]} &am…...

《前端开发者的进阶之路》

前端作为Web开发的重要领域之一&#xff0c;不断地发展和演变着。除了基本的HTML、CSS、JavaScript技能&#xff0c;前端开发者需要掌握更多的进阶知识才能应对不断变化的需求。本文将介绍一些前端的进阶知识&#xff0c;帮助前端开发者进一步提高自己的技能水平。1.框架和库在…...

为什么说网络安全是风口行业?是IT行业最后的红利?

前言 “没有网络安全就没有国家安全”。当前&#xff0c;网络安全已被提升到国家战略的高度&#xff0c;成为影响国家安全、社会稳定至关重要的因素之一。 网络安全行业特点 1、就业薪资非常高&#xff0c;涨薪快 2021年猎聘网发布网络安全行业就业薪资行业最高人均33.77万&…...

使用shell 脚本,批量解压一批zip文件,解压后的文件放在以原zip文件名前10个字符的文件夹中的例子

#!/bin/bash for file in *.zip dofolder$(echo $file | cut -c 1-10)mkdir $folderunzip -q $file -d $folder doneecho "All zip files have been extracted." # 说明&#xff1a; # 1. for循环遍历当前目录下的所有zip文件 # 2. 使用cut命令提取zip文件名前10个字…...

01 | Msyql系统架构

目录MySQL系统架构连接器查询缓存分析器优化器执行器MySQL系统架构 大体来说&#xff0c;MySQL分为Server层和引擎层两部分。 Server层包含链接器、查询缓存、分析器、优化器和执行器&#xff0c;而引擎层负责的是数据的存储和读取&#xff0c;支持InnoDB、Myisam、Memory等多…...

Linux命令---设备管理

Linux setleds命令Linux setleds命令用来设定键盘上方三个 LED 的状态。在 Linux 中&#xff0c;每一个虚拟主控台都有独立的设定。语法setleds [-v] [-L] [-D] [-F] [{|-}num] [{|-}caps] [{|-}scroll]参数&#xff1a;-F&#xff1a;预设的选项&#xff0c;设定虚拟主控台的状…...

前端入门:HTML5+CSS3+JAAVASCRIPT

1、 初识HTML HTML:Hyper Text Markup Language(超文本标记语言) 。 超文本包括&#xff1a;文字、图片、音频、视频、动画等。 1.1、W3C标准 1.2、HTML基本结构 示例&#xff1a; <!-- DOCTYPE:告诉浏览器&#xff0c;我们要使用什么规划&#xff0c;这里是HTML --> …...

【头歌实验】课外作业一:开通ECS及使用Linux命令

文章目录一、完成下列实验并截图二、简要回答“课堂考核”内容三、在头歌、华为云或阿里云官网上&#xff0c;找出自己的课外学习资源&#xff0c;制定小组的课程学习计划、专业学习计划。四、习题1.10一、完成下列实验并截图 1、实验《ECS云服务器新手上路》 https://develo…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中&#xff0c;手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力&#xff0c;既支持点击、长按、拖拽等基础单一手势的精细控制&#xff0c;也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)

目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关&#xff0…...

回溯算法学习

一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

面试高频问题

文章目录 &#x1f680; 消息队列核心技术揭秘&#xff1a;从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"&#xff1f;性能背后的秘密1.1 顺序写入与零拷贝&#xff1a;性能的双引擎1.2 分区并行&#xff1a;数据的"八车道高速公路"1.3 页缓存与批量处理…...