单例模式--理解

单例模式

单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。

单例模式的类型

单例模式有两种类型:

  • 懒汉式:在真正需要使用对象时才去创建该单例类对象
  • 饿汉式:在类加载时已经创建好该单例对象,等待被程序使用

 懒汉式

懒汉式创建对象的方法是在程序使用对象前,先判断该对象是否已经实例化(判空),若已实例化直接返回该类对象。,否则则先执行实例化操作。

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

饿汉式

饿汉式在类加载时已经创建好该对象,在程序调用时直接返回该单例对象即可,即我们在编码时就已经指明了要马上创建这个对象,不需要等到被调用时再去创

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

注意上面的代码在第3行已经实例化好了一个Singleton对象在内存中,不会有多个Singleton对象实例存在

类在加载时会在堆内存中创建一个Singleton对象,当类被卸载时,Singleton对象也随之消亡了。

 

线程安全

懒汉式和饿汉式谁是线程安全的?

饿汉是,懒汉不是。

原因,我们一个一个看

这个方法其实是存在问题的,试想一下,如果两个线程同时判断singleton为空,那么它们都会去实例化一个Singleton对象,这就变成双例了。所以,我们要解决的是线程安全问题。

原子性用synchronized来保证,if是判断再赋值的不是原子的

public static synchronized Singleton getInstance() {if (singleton == null) {singleton = new Singleton();}return singleton;
}
// 或者
public static Singleton getInstance() {synchronized(Singleton.class) {   if (singleton == null) {singleton = new Singleton();}}return singleton;
}

这样就规避了两个线程同时创建Singleton对象的风险

原子性保证了,还有重排序和可见性的问题。

使用volatile防止指令重排
创建一个对象,在JVM中会经过三步:

(1)为singleton分配内存空间

(2)初始化singleton对象

(3)将singleton指向分配好的内存空间

指令重排序是指:JVM在保证最终结果正确的情况下,可以不按照程序编码的顺序执行语句,尽可能提高程序的性能

在这三步中,第2、3步有可能会发生指令重排现象,创建对象的顺序变为1-3-2,会导致多个线程获取对象时,有可能线程A创建对象的过程中,执行了1、3步骤,线程B判断singleton已经不为空,获取到未初始化的singleton对象,就会报NPE异常。文字较为晦涩,可以看流程图:



            链接:https://blog.csdn.net/weixin_41949328/article/details/107296517

使用volatile关键字修饰的变量,可以保证其内存可见性,即每一时刻线程读取到该变量的值都是内存中最新的那个值,线程每次操作该变量都需要先读取该变量。

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

这个代码已经基本安全了,接下来考虑效率的问题。

每次去获取对象都需要先获取锁,并发性能非常地差,极端情况下,可能会出现卡顿现象。

如果没有实例化对象则加锁创建,如果已经实例化了,则不需要加锁,直接获取实例

所以直接在方法上加锁的方式就被废掉了,因为这种方式无论如何都需要先获取锁

public static Singleton getInstance() {if (singleton == null) {  // 线程A和线程B同时看到singleton = null,如果不为null,则直接返回singletonsynchronized(Singleton.class) { // 线程A或线程B获得该锁进行初始化if (singleton == null) { // 其中一个线程进入该分支,另外一个线程则不会进入该分支singleton = new Singleton();}}}return singleton;
}

 

代码已经完美地解决了并发安全+性能低效问题:

第2行代码,如果singleton不为空,则直接返回对象,不需要获取锁;而如果多个线程发现singleton为空,则进入分支;
第3行代码,多个线程尝试争抢同一个锁,只有一个线程争抢成功,第一个获取到锁的线程会再次判断singleton是否为空,因为singleton有可能已经被之前的线程实例化
其它之后获取到锁的线程在执行到第4行校验代码,发现singleton已经不为空了,则不会再new一个对象,直接返回对象即可
之后所有进入该方法的线程都不会去获取锁,在第一次判断singleton对象时已经不为空了

 枚举实现

public enum Singleton {INSTANCE;public void doSomething() {System.out.println("这是枚举类型的单例模式!");}
}

使用枚举实现单例模式的优势在哪里?

我们从最直观的地方入手,第一眼看到这几行代码,就会感觉到“少”,没错,就是少,虽然这优势有些牵强,但写的代码越少,越不容易出错。

优势1:代码对比饿汉式与懒汉式来说,更加地简洁

其次,既然是实现单例模式,那这种写法必定满足单例模式的要求,而且使用枚举实现时,没有做任何额外的处理。

优势2:它不需要做任何额外的操作去保证对象单一性与线程安全性

我写了一段测试代码放在下面,这一段代码可以证明程序启动时仅会创建一个 Singleton 对象,且是线程安全的。

我们可以简单地理解枚举实现单例的过程:在程序启动时,会调用Singleton的空参构造器,实例化好一个Singleton对象赋给INSTANCE,之后再也不会实例化

总结

(1)单例模式常见的写法有两种:懒汉式、饿汉式

(2)懒汉式:在需要用到对象时才实例化对象,正确的实现方式是:Double Check + Lock,解决了并发安全和性能低下问题

(3)饿汉式:在类加载时已经创建好该单例对象,在获取单例对象时直接返回对象即可,不会存在并发安全和性能问题。

(4)在开发中如果对内存要求非常高,那么使用懒汉式写法,可以在特定时候才创建该对象;

(5)如果对内存要求不高使用饿汉式写法,因为简单不易出错,且没有任何并发安全和性能问题

(6)为了防止多线程环境下,因为指令重排序导致变量报NPE,需要在单例对象上添加volatile关键字防止指令重排序

(7)最优雅的实现方式是使用枚举,其代码精简,没有线程安全问题,且 Enum 类内部防止反射和反序列化时破坏单例。

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

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

相关文章

使用docker-compose创建多项目容器运行

使用docker-compose创建多项目容器运行 按招网友提供方法创建 docker-compose.yml内容(这里改了桥接模式,并且注释放开) version: "3" services:docker_python:image: python:2.7.18container_name: py_appworking_dir: "/r…

婴儿洗衣机哪种比较实用?精选四大热门口碑婴儿洗衣机推荐

对于有了宝宝的家庭来说,洗衣成为了一项重要的家务事。大家都知道,宝宝的皮肤比较娇嫩,容易受到各种细菌、病毒的侵扰。所以,宝宝的衣物应该与大人的分开洗。婴儿洗衣机作为一种专门为婴幼儿家庭设计的洗衣机,其具有除…

空调(c++实现)

题目 Farmer John 的 N 头奶牛对他们牛棚的室温非常挑剔。 有些奶牛喜欢温度低一些,而有些奶牛则喜欢温度高一些。 Farmer John 的牛棚包含一排 N个牛栏,编号为 1…N,每个牛栏里有一头牛。 第 i 头奶牛希望她的牛栏中的温度是 pi&#xff0c…

nginx部署前端教程

目录 一、前言二、部署三、注意四、参考 一、前言 一般来说现在的软件项目,都是分用户端以及管理端的,并且是前后端分离的,这里我来记录一下部署两个前端的教程。 部署前端之前需要的准备工作是部署springBoot后端程序,这里我do…

qt设置异形图片并实现透明效果

思路:将背景设置为透明,然后将图片设置给label,将laben和this都设置为图片大小 setAttribute(Qt::WA_TranslucentBackground, true); 可以将背景设置为透明 然后 QPixmap *pixnew QPixmap(":/Image/xxx.png"); this->setFixedSize(pix->width(),pix->…

对OceanBase中的配置项与系统变量,合法性检查实践

在“OceanBase 配置项&系统变量实现及应用详解”的系列文章中,我们已经对配置项和系统变量的源码进行了解析。当涉及到新增配置项或系统变量时,通常会为其指定一个明确的取值范围或定义一个专门的合法性检查函数。本文将详细阐述在不同情境下&#x…

深入理解指针2:数组名理解、一维数组传参本质、二级指针、指针数组和数组指针、函数中指针变量

目录 1、数组名理解 2、一维数组传参本质 3、二级指针 4、指针数组和数组指针 5、函数指针变量 1、数组名理解 首先来看一段代码: int main() {int arr[10] { 1,2,3,4,5,6,7,8,9,10 };printf("%d\n", sizeof(arr));return 0; } 输出的结果是&…

[大模型]大语言模型量化方法对比:GPTQ、GGUF、AWQ

在过去的一年里,大型语言模型(llm)有了飞速的发展,在本文中,我们将探讨几种(量化)的方式,除此以外,还会介绍分片及不同的保存和压缩策略。 说明:每次加载LLM示例后,建议清除缓存,以…

Jupyter Notebook中常见的快捷键

Jupyter Notebook的快捷键主要分为两种模式:命令模式和编辑模式。 在命令模式下,键盘输入用于运行程序命令,此时单元格框线是蓝色的; 在编辑模式下,可以往单元格中键入代码或文本,此时单元格框线是绿色的…

MySQL的Seconds_Behind_Master 是如何计算的

Seconds_Behind_Master 如何计算 以下是源码中关于延迟时间计算方法的注释说明 # 位于rpl_mi.h中定义clock_diff_with_master附近(翻阅了5.6.34和5.7.22 两个版本,对于复制延迟的计算公式两者一致) # 从源码注释上来看,复制延迟…

直通车人群和引力魔方人群的区别

1)直通车的人群,他和引力魔方的人群看起来一样,但是作用不一样,直通车广告是通过关键词筛选流量的,人群只是起到溢价作用,引力魔方人群是筛选作用出价作用; 2)直通车的人群&#xf…

python之列表操作

1、创建列表 代码示例: i [1, 2, 34, 4] o list((1, 2, 3, 4, 5, 6)) 分别创建了两个数组,这两种格式都能创建数组 2、关于数组的操作 1、添加元素 1、append() append方法主要是添加一个元素 代码示例如下:…

深度学习理论基础(七)Transformer编码器和解码器

学习目录: 深度学习理论基础(一)Python及Torch基础篇 深度学习理论基础(二)深度神经网络DNN 深度学习理论基础(三)封装数据集及手写数字识别 深度学习理论基础(四)Parse…

手机软件何时统一--桥接模式

1.1 凭什么你的游戏我不能玩 2007年苹果手机尚未出世,机操作系统多种多样(黑莓、塞班、Tizen等),互相封闭。而如今,存世的手机操作系统只剩下苹果OS和安卓,鸿蒙正在稳步进场。 1.2 紧耦合的程序演化 手机…

vue的 blob文件下载文件时,后端自定义异常,并返回json错误提示信息,前端捕获信息并展示给用户

1.后端返回的json数据结构为: {"message":"下载失败,下载文件不存在,请联系管理员处理!","code":500} 2.vue 请求后台接口返回的 Blob数据 3.问题出现的原因是,正常其他数据列表接口&…

[C++][算法基础]堆排序(堆)

输入一个长度为 n 的整数数列,从小到大输出前 m 小的数。 输入格式 第一行包含整数 n 和 m。 第二行包含 n 个整数,表示整数数列。 输出格式 共一行,包含 m 个整数,表示整数数列中前 m 小的数。 数据范围 1≤m≤n≤&#x…

第4章 Redis,一站式高性能存储方案,笔记问题

点赞具体要实现功能有哪些? 可以点赞的地方:对帖子点赞,对评论点赞点一次是点赞,再点一次是取消赞统计点赞的数量(计数,string),帖子被点赞的数量,某个用户被点赞的数量…

【数据结构】考研真题攻克与重点知识点剖析 - 第 5 篇:树与二叉树

(考研真题待更新) 欢迎订阅专栏:408直通车 请注意,本文中的部分内容来自网络搜集和个人实践,如有任何错误,请随时向我们提出批评和指正。本文仅供学习和交流使用,不涉及任何商业目的。如果因本…

2024免费Mac电脑用户的系统清理和优化软件CleanMyMac

作为产品营销专家,对于各类产品的特性与优势有着深入的了解。CleanMyMac是一款针对Mac电脑用户的系统清理和优化软件,旨在帮助用户轻松管理、优化和保护Mac电脑。以下是关于CleanMyMac的详细介绍: CleanMyMac X2024全新版下载如下: https://…

阿里云乱扣费故障,技术堪忧

2024年4月3日,距离2023年11月的故障没有多久,阿里云又出现乱扣费故障,导致账号欠费3000多,oss,块存储,cdn等所有后付费服务停止工作,不知道这个故障能算什么级别的。 凌晨1点多,收到…