【设计模式】详解单例设计模式(包含并发、JVM)
文章目录
- 1、背景
- 2、单例模式
- 3、代码实现
- 1、第一种实现(饿汉式)
- 为什么属性都是static的?
- 2、第二种实现(懒汉式,线程不安全)
- 3、第三种实现(懒汉式,线程安全)
- 4、第四种实现(懒汉式,双重校验锁DCL)
- getSingleton里为什么会有两个if判空?
- singleton为什么被volatile修饰
1、背景
在软件开发中,经常需要某些类只能有唯一的实例,比如数据库连接。如何才能保证整个应用中只有一个唯一实例?如果靠人为制定的协定来约束,显然不能很好的保证这一点。如果要从语法上约束,在面向对象里面,什么地方能够约束实例的创建?
显然,只有构造函数与类实例的创建相关。那么如何才能让构造函数阻止类实例的创建,使其只有一个唯一实例?让构造函数的修饰为私有
2、单例模式
单例模式是设计模式中最简单的形式之一。这一模式的目的是使得类的一个对象成为系统中的唯一实例。要实现这一点,可以从客户端对其进行实例化开始。因此需要用一种只允许生成对象类的唯一实例的机制,“阻止”所有想要生成对象的访问。
3、代码实现
1、第一种实现(饿汉式)
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; }
}
饿汉式在类加载时已经创建好该对象,在程序调用时直接返回该单例对象即可。类在加载时会在JVM的堆内存中创建一个Singleton对象,当类被卸载时,Singleton对象也随之消亡了(没有被引用)。
它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。
为什么属性都是static的?
为了使其只有一个唯一实例,我们将无参构造方法设置成静态的,其他的类就没有办法直接通过new来构建该类的对象。无法通过new来构建类对象,那就只能通过调用类的静态方法getInstance这个唯一进出口来获得对象。由于静态方法里只能使用静态(static)属性,所以instance被修饰为静态的(static)。
2、第二种实现(懒汉式,线程不安全)
public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
}
这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。
为什么不支持多线程?
假如现在有两个线程,一个线程a,一个线程b,线程a被操作系统选中,分配时间片,去调用Singleton.getInstance(),判断instance== null,为true,进入代码块,然后时间片用完,切换到线程b执行,线程b也调用Singleton.getInstance(),当运行到instance==null时,因为线程a上次判断完instance为null,就结束了,所以此时instance还是null,于是线程b执行instance=new Singleton(),并返回instance。线程b时间片用完,让出cpu,线程a被选中,从instance=new Singleton开始执行, 于是又在堆中创建了一个Singleton对象实例,并返回。所以严格意义上说它并不算单例模式。
3、第三种实现(懒汉式,线程安全)
public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance(){ if (instance == null) { instance = new Singleton(); } return instance; }
}
这样就规避了两个线程同时创建Singleton对象的风险,但是引来另外一个问题:每次去获取对象都需要先获取锁,并发性能非常地差,极端情况下,可能会出现卡顿现象。因为在getInstance方法上加了synchronized锁,调用该方法的线程需要排队调用。(一个线程获得了Singleton类锁后,在该线程执行完getInstance()方法前,其他线程要么阻塞,要么自旋,无法执行getInstance方法)
4、第四种实现(懒汉式,双重校验锁DCL)
public class Singleton { private volatile static Singleton singleton; //1private Singleton (){} //2public static Singleton getSingleton() { //3 if (singleton == null) { //4synchronized (Singleton.class) { //5 if (singleton == null) { //6singleton = new Singleton(); //7 } } } return singleton; }
}
第四种实现对于第三种实现进行了优化,如果没有实例化对象则加锁创建,如果已经实例化了,则不需要加锁,直接获取实例,这种优化提高了高并发下的性能。
getSingleton里为什么会有两个if判空?
假设现在有3个线程:线程A、线程B、线程C。线程A执行第一个if判断为空后,时间片用完让出cpu,切换线程。线程B执行第一个if判断为空后,加锁,加锁后恰好时间片用完,让出cpu。切换到线程C ,判断完第一个if后,因为B没有释放锁,等待时间片结束(cpu空转),切换线程。当再次切换到线程B后,进入同步代码块,先判断singleton是不是null,是null,创建对象,释放锁,返回对象。当执行完第一个if判断后阻塞在加锁的线程 ,抢到锁后,继续执行剩下的代码,在第二个if判断时,因为第一个线程已经创建了对象,则跳过,退出同步代码块,释放锁,得到线程B创建的实例对象。后面再来的线程在第一个if判断返回false后,直接返回线程B创建好的实例对象。
由此可见:第一个if是为了验证是否已经创建了对象,该判断是为了避免不必要的同步,第二个if是为了避免重复创建单例,是给第二个以及后面持有锁的线程准备的。
singleton为什么被volatile修饰
什么是指令重排序:JVM在保证最终结果正确的情况下,可以不按照程序编码的顺序执行语句,尽可能提高程序的性能。
new对象操作在指令层面不是一个原子操作,分为三步:
- 为singleton分配内存空间M
- singleton初始化
- 将singleton指向分配好的内存空间M
在执行new操作时,2、3步可能发生指令重排,会发生下面这种情况:申请空间后,存入地址,但singleton对象还未初始化,切换线程。当新来的线程进行第一个if判断时,因为方法区中singleton里面是有堆中地址的,判断不为null,当return时,因为申请的空间中没有数据,则报空指针异常。
如下图:

使用volatile关键字可以防止指令重排序。使用volatile关键字修饰的变量,可以保证其指令执行的顺序与程序指明的顺序一致,不会发生顺序变换。使用volatile关键字修饰的变量,可以保证其内存可见性,即每一时刻线程读取到该变量的值都是内存中最新的那个值,线程每次操作该变量都需要先读取该变量。
我在这里对堆、方法区、Java虚拟机栈进行了详解😄😄😄😄
相关文章:
【设计模式】详解单例设计模式(包含并发、JVM)
文章目录 1、背景2、单例模式3、代码实现1、第一种实现(饿汉式)为什么属性都是static的?2、第二种实现(懒汉式,线程不安全)3、第三种实现(懒汉式,线程安全)4、第四种实现…...
监控和可观察性在 DevOps 中的作用!
在不断发展的DevOps世界中,深入了解系统行为、诊断问题和提高整体性能的能力是首要任务之一。监控和可观察性是促进这一过程的两个关键概念,为系统的健康状况和性能提供有价值的可见性。虽然这些术语经常互换使用,但它们代表了理解和管理复杂…...
论文分享:PowerTCP: Pushing the Performance Limits of Datacenter Networks
1 原论文的题目(中英文)、题目中包含了哪些关键词?这些关键词的相关知识分别是什么? 题目:PowerTCP: Pushing the Performance Limits of Datacenter Networks PowerTCP:逼近数据中心的网络性能极限 2 论…...
浏览器的同源策略 - 跨域问题
1.什么是跨域 跨域问题的实质是浏览器的同源策略造成的。浏览器同源策略是浏览器为 JavaScript 施加的限制。简单点说就是非同源会出现如下等限制: 无法访问其他源下的网页的 Cookies,Storage等;无法访问其他源下的DOM对象和 JS 对象;无法使…...
go 查询采购单设备事项[小示例]V2-两种模式{严格,包含模式}
第一版: https://mp.csdn.net/mp_blog/creation/editor/131979385 第二版: 优化内容: 检索数据的两种方式: 1.严格模式--找寻名称是一模一样的内容,在上一个版本实现了 2.包含模式,也就是我输入检索关…...
c++11 标准模板(STL)(std::basic_filebuf)(八)
定义于头文件 <fstream> template< class CharT, class Traits std::char_traits<CharT> > class basic_filebuf : public std::basic_streambuf<CharT, Traits> std::basic_filebuf 是关联字符序列为文件的 std::basic_streambuf 。输入序…...
行为型模式之解释器模式
解释器模式(Interpreter Pattern) 解释器模式(Interpreter Pattern)是一种行为设计模式,它用于对语言的文法进行解释和解析,以实现特定的操作。 在解释器模式中,存在以下几个角色: 抽…...
阿里云域名备案
最好的爱情,不是因为我们彼此需要在一起,而是因为我们彼此想要在一起。 阿里云的域名如何备案,域名备案和ICP备案一样吗?? 截至我所掌握的知识(2021年9月),阿里云的域名备案和ICP备案…...
Clion开发Stm32之温湿度传感器(DS18B20)驱动编写和测试
前言 涵盖之前文章: Clion开发STM32之HAL库GPIO宏定义封装(最新版)Clion开发stm32之微妙延迟(采用nop指令实现)Clion开发STM32之日志模块(参考RT-Thread) DSP18B20驱动文件 头文件 /*******************************************************************************Copy…...
文档管理NAS储存安全吗?
关键词:私有化、知识管理系统、文档管理、群晖NAS、协同编辑 随着企业不断发展扩大,企业的知识文档也逐渐增多,很多企业方便管理及考虑数据安全问题会将文件数据储存至NAS。 但将企业文档数据放在NAS上就足够安全的吗? 天翎文档管…...
用windeployqt.exe打包Qt代码
首先找到我们编译Qt代码的对应Qt版本的dll目录,该目录下有windeployqt.exe: D:\DevTools\Qt\5.9\msvc2017_64\bin 在这个目录下打开cmd程序。 然后把要打包的exe放到一个单独的目录下,比如: 然后在cmd中调用: winde…...
【Python机器学习】实验04(2) 机器学习应用实践--手动调参
文章目录 机器学习应用实践1.1 准备数据此处进行的调整为:要所有数据进行拆分 1.2 定义假设函数Sigmoid 函数 1.3 定义代价函数1.4 定义梯度下降算法gradient descent(梯度下降) 此处进行的调整为:采用train_x, train_y进行训练 1.5 绘制决策边界1.6 计算…...
【爬虫案例】用Python爬取iPhone14的电商平台评论
用python爬取某电商网站的iPhone14评论数据, 爬取目标: 核心代码如下: 爬取到的5分好评: 爬取到的3分中评: 爬取到的1分差评: 所以说,用python开发爬虫真的很方面! 您好&…...
01)docker学习 centos7离线安装docker
docker学习 centos7离线安装docker 在实操前可以先看下docker教程,https://www.runoob.com/docker/docker-tutorial.html , 不过教程上都是在线安装方式,很方便,离线安装肯定比如在线麻烦点。 一、什么是Docker 在学习docker时,在网上看到一篇博文讲得很好,自己总结一下…...
前端 - 实习两个星期总结
文章目录 吐槽总结新人建议项目学习到的 今天已经是菜鸟实习的第二个星期了,怎么说呢,反正就是进的一个不大不小的厂,做着不难不易的事,菜鸟现在主要做的就是适配!现在就来总结一下,不过这之前,…...
MySQL——主从复制
1.理解MySQL主从复制原理。 2.完成MySQL主从复制。 1.理解MySQL主从复制原理。 1)、MySQL支持的复制类型 (1)、基于语句( statement )的复制 在主服务器上执行SQL 语句,在从服务器上执行同样的语句。 My…...
报表下载工具
1.需求说明 我有一堆文件的Url地址, 现在需要按照企业,项目和报表类型分类下载到对应的文件夹中 2.相关实体类 企业文件夹定义 package com.vz.utils.report;import lombok.Data; import java.util.ArrayList; import java.util.List; import java.uti…...
树及其遍历
文章目录 树树定义专业术语树分类 二叉树分类存储连续存储(完全二叉树)链式存储一般树的存储森林的存储 线索二叉树哈夫曼树构造步骤 遍历先序遍历中序遍历后续遍历 链式二叉树遍历具体代码已知两种遍历序列求原始二叉树已知先序和中序求后序已知中序和后…...
Qt报错解决办法
anaconda环境安装qt报错解决办法 报错:thresholdGap: 20 pointsShape: 164142 qt.qpa.plugin: Could not find the Qt platform plugin “wayland” in “/home/tianhailong/anaconda3/envs/edge_algorithm/lib/python3.8/site-packages/cv2/qt/plugins” This app…...
Python(四十七)列表对象的创建
❤️ 专栏简介:本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中,我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 :本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
