单例模式.

目录

♫什么是单例模式

♫饿汉式单例模式

♫懒汉式单例模式

♫单例模式的线程安全问题

♪原子性

♪内存可见性与指令重排序


♫什么是单例模式

单例模式是一种设计模式,通过巧用Java的现有语法,实现一个只能被创建一个实例的类,并提供一个全局访问点。在有些创场景中,一些特点的类只能创建一个实例,虽然不依赖单例模式我们也可以控制类的实现个数,但通过单例模式实现的类就相当于有了语法约束,即使想要实现多个实例都很难。在Java中,单例模式的实现有多种方式,下面是两种比较常用的实现方式。

♫饿汉式单例模式

饿汉式单例模式是指在类加载时就创建实例对象,通过static关键字保证在程序的整个生命周期中只存在一个实例对象:

public class Singleton {// 静态成员变量,用来记录唯一实例private static Singleton uniqueInstance = new Singleton();// 私有构造方法,防止外部通过new关键字创建实例private Singleton() {}// 静态工厂方法,返回唯一实例public static Singleton getInstance() {return uniqueInstance;}
}

通过将构造方法设置为私有的,保证类外无法通过new来创建实例的同时,通过static将uniqueInstance成员属性修饰为类属性(Java代码中的每个类在编译完成后都会生成.class文件,JVM加载时通过读取.class文件中的二进制指令来在内存中构造出类对象(Singleton.class),类对象的属性就是类属性),由于类对象只有一份,故类属性也就只有一份。

♫懒汉式单例模式

懒汉式单例模式是指在第一次访问时才创建实例对象,在第一次访问之前则不创建对象:

public class Singleton {// 静态成员变量,用来记录唯一实例private static Singleton uniqueInstance = null;// 私有构造方法,防止外部通过new关键字创建实例private Singleton() {}// 静态工厂方法,返回唯一实例public static synchronized Singleton getInstance() {if (uniqueInstance == null) {uniqueInstance = new Singleton();}return uniqueInstance;}
}

懒汉模式与饿汉模式的区别在于懒汉模式只有在第一次使用时才创建实例对象,不使用则不创建实例对象,而饿汉模式则不管有没有使用都会在使用前(类加载时)创建一个实例对象。

♫单例模式的线程安全问题

在多线程环境下,饿汉模式只有到读操作不需要考虑线程安全问题,而懒汉模式既有读又有写,这就得涉及到线程安全了。

1.在if语句中可以分为读、比较、写三步,由于这三步骤不是原子性的,在多线程环境下就可能发生第一个线程读完还未写入前,第二个线程也开始读,从而导致可能多次执行new操作。

♪原子性

我们可以通过synchronized来保证读、比较、写的原子性:

//懒汉模式
class Singleton2 {private static Singleton2 uniqueInstance = null;public Singleton2 getInstance() {synchronized (Singleton2.class) {if (uniqueInstance == null) {uniqueInstance = new Singleton2();}}return uniqueInstance;}private Singleton2() {}
}

虽然通过给类对象加锁保证了if语句的原子性,但这样写每一次使用都需要进行加锁操作加大了开销,故我们还可以在加锁前再判断下需不需要进行加锁操作:

//懒汉模式
class Singleton2 {private static Singleton2 uniqueInstance = null;public Singleton2 getInstance() {if (uniqueInstance == null) {synchronized (Singleton2.class) {if (uniqueInstance == null) {uniqueInstance = new Singleton2();}}}return uniqueInstance;}private Singleton2() {}
}

注:第一个if判断是否需要加锁,第二个if判断是否需要创建对象

♪内存可见性与指令重排序

懒汉模式不仅有原子性问题还有内存可见性与指令重排序的隐患:

♩内存可见性:在多线程环境下,有多个线程同时进行getInstance操作,有可能编译器只有第一次是从内存中读取uniqueInstance的值,其它时候都是直接从寄存器或cache中读取uniqueInstance的值,也会导致多次创建实例对象。

♩指令重排序:uniqueInstance=new Singleton2()操作可以拆分为三步:①.申请内存空间②.调用构造方法在内存空间上创建一个实例对象③.把内存空间的值赋值给uniqueInstance。正常情况下顺序执行①②③是没有问题的,但无法保证编译器不会优化这三步骤的执行顺序,若是执行顺序为①③②,那么就可能在一个线程执行到②之前就调度去另一个线程执行①,这就会导致内存空间后面被初始化,而该线程就以为对象创建好了,如果这时候这个线程使用对象的属性方法就会出现问题。

解决内存可见性与指令重排序就需要给uniqueInstance加上volatile关键字:

//懒汉模式
class Singleton2 {private static volatile Singleton2 uniqueInstance = null;public Singleton2 getInstance() {if (uniqueInstance == null) {synchronized (Singleton2.class) {if (uniqueInstance == null) {uniqueInstance = new Singleton2();}}}return uniqueInstance;}private Singleton2() {}
}

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

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

相关文章

Vue3:将表格数据下载为excel文件

需求 将表格数据或者其他形式的数据下载为excel文件 技术栈 Vue3、ElementPlus、 实现 1、安装相关的库 下载xlsx 和 file-saver 库 npm install -S file-saver npm install -S xlsx引入XLSX库和FileSaver库 import XLSX from xlsx; import FileSaver from file-saver;…

vue3响应式对象:ref和reactive

ref() <template><button click"changeMsg">改变信息</button><div>{{ msg }}</div><div>{{ man }}</div> </template><script lang"ts"> import { defineComponent,ref,Ref } from vueexport def…

Android 13.0 根据包名授权悬浮窗权限

1.概述 在13.0的系统产品开发中,在一些特殊权限比如悬浮窗,WRITE_SETTINGS权限,安装权限等等这些特殊权限,是需要单独授权的,在申请动态权限是不起作用的,所以 就需要根据包名用AppOpsManager.java中的方法来授权,来实现授权悬浮窗权限的功能. 2.根据包名授予悬浮窗权…

【SEC 学习】美化 Linux 终端

一、步骤 1. 进入 /etc/bash.bashrc vim /etc/bash.bashrc2. 重新加载 bash.bashrc source /etc/bash.bashrc二、各参数指标 符号含义\u当前用户的账号名称\h仅取主机的第一个名字&#xff0c;如上例&#xff0c;则为fc4&#xff0c;.linux则被省略\H完整的主机名称。例如&…

服务器感染了.secret勒索病毒,如何确保数据文件完整恢复?

导言&#xff1a; .secret勒索病毒已经成为网络安全界的一大噩梦。这种病毒会将您宝贵的数据文件加密&#xff0c;然后以高额赎金作为条件来释放它们。在这篇文章中&#xff0c;我们将深入研究.secret勒索病毒的特点&#xff0c;讨论如何解密被锁定的数据文件&#xff0c;并提…

C++STL---Vector、List所要掌握的基本知识

绪论​ 拼着一切代价&#xff0c;奔你的前程。 ——巴尔扎克&#xff1b;本章主要围绕vector和list的使用&#xff0c;以及容器底层迭代器失效问题&#xff0c;同时会有对原码的分析和模拟实现其底层类函数。​​​​话不多说安全带系好&#xff0c;发车啦&#xff08;建议电脑…

SQL中使用ROLLUP和CUBE函数轻松生成汇总行

在数据分析和报表制作中&#xff0c;通常需要对数据进行汇总和分组&#xff0c;我们常用的就是GROUP BY汇总数据&#xff0c;当我们想按照不同维度汇总时&#xff0c;往往需要编写多个GROUP BY预计&#xff0c;而借助ROLLUP 和 CUBE 函数可以一次性生成子总计和总计行&#xff…

Mac电脑配置Dart编程环境

1.安装Dart SDK 官网地址&#xff1a;https://dart.dev/get-dart $brew tap dart-lang/dart$brew install dart 安装后&#xff0c;用命令检测一下是否安装正常。 $brew info dart 2.VS Code配置Dart环境 1).安装VS Code 官网地址&#xff1a;https://code.visualstudio.c…

Python环境下LaTeX数学公式转图像方案调研与探讨

目录 引言方案一&#xff1a;基于LaTeX环境方案二&#xff1a;基于KaTeX(推荐) 方案三&#xff1a;基于Matplotlib写在最后 引言 近来&#xff0c;涉及到一些公式识别的项目&#xff0c;输入是公式的图像&#xff0c;输出是LaTeX格式的数学公式字符串。 这类项目一般都采用深…

【SEC 学习】Vim 的基本使用

一、Vim 编辑器安装 yum install -y vim二、Vim 三种模式 命令模式 编辑模式 末行模式 三、三种模式之间的转换 1. 命令模式 -> 编辑模式 快捷键含义i从光标处插入I从光标所在行首插入a从光标后插入A从光标所在行末插入o从光标下一行插入O从光标上一行插入 2. 命令模式 …

pgsql 分组查询,每组取10条

需求&#xff1a; 按照表的字段分组&#xff0c;然后每组取10条结果&#xff0c;返回即可 sql 如下&#xff1a; SELECT* FROM (SELECT chk_id,feature_id,task_id, ROW_NUMBER () OVER (PARTITION BY chk_id ORDER BY chk_id) AS row_num FROM ics_check_report WHERE task…

Ubuntu 23.10(Mantic Minotaur)正式发布,支持Linux 6.5和GNOME 45

导读Canonical 近日正式发布了 Ubuntu 23.10&#xff08;Mantic Minotaur&#xff09;操作系统&#xff0c;其中包含一些最新的 GNU/Linux 技术、改进的硬件支持以及许多其他变化。 Ubuntu 23.10 采用了最新的 Linux 6.5 内核系列&#xff0c;并为 Ubuntu 桌面和服务器增强了 z…

Qt5 Python-docx库的使用,Qt python混合编程,qt 读写word,不依赖office

解决方案的选择 参考&#xff1a; https://www.jianshu.com/p/be68884849c3 因为项目要求不能使用模板方案&#xff0c;不能依赖Office&#xff0c;网上找了一些解决方案进行调研&#xff0c;以下几个方案&#xff1a; OpenOffice: 兼容性差&#xff0c;集成调用难度大LibOffi…

从字节码层面分析Lambda

invokedynamic 指令 动态类型语言和静态类型语言的区别&#xff1a; ● 它俩的区别在于对类型的检查是在编译器还是在运行期&#xff0c;满足前者就是静态类型语言&#xff0c;反之是动态类型语言。 ● 静态类型语言是判断变量自身的类型信息&#xff1b;动态类型语言是判断变…

【顺序栈的出栈,链栈的表示和实现,递归定义】

文章目录 顺序栈的出栈 链栈的表示和实现链表的初始化判断链栈是否为空链栈的入栈链栈的出栈 递归定义函数的调用过程 顺序栈的出栈 &#xff08;1&#xff09;判断是否栈空&#xff0c;若空则出错&#xff08;下溢&#xff09;。 &#xff08;2&#xff09;获取栈顶元素e。 &…

【AD9361 数字接口CMOS LVDSSPI】B 并行数据之CMOS 续

续【AD9361 数字接口CMOS &LVDS&SPI】B 并行数据之CMOS 数据总线空闲和周转周期 &#xff08;CMOS&#xff09; P0_D[11&#xff1a;0]和P1_D[11&#xff1a;0]总线信号通常由BBP或AD9361有源驱动。在任何空闲期间&#xff0c;两个组件都会忽略数据总线值。但是&…

MODBUS-RTU从站通信(SMART PLC作为MODBUS-RTU从站)

SMART PLC作为MODBUS-RTU主站通信请参考下面文章链接: 【精选】PLC MODBUS通信优化、提高通信效率避免权限冲突(程序+算法描述)-CSDN博客文章浏览阅读2.5k次,点赞5次,收藏10次。MODBUS通讯非常简单、应用也非常广泛,有些老生常谈的问题,这里不再赘述,感兴趣的可以参看…

uci机器学习数据库简介

UCI&#xff08;University of California, Irvine&#xff09;机器学习数据库是经过精心整理的、用于研究和开发机器学习算法的数据集合。UCI机器学习数据库是一个公开的、广泛使用的数据集合&#xff0c;它由加州大学欧文分校的计算机科学系维护。该数据库中包含了许多数据集…

Python中的变量与注释

一、变量与注释决定第一印象 1、变量和注释是代码里最接近自然语言的部分&#xff0c;其可读性至关重要。 2、即便是同一个算法&#xff0c;变量和注释的不同&#xff0c;也会给人截然不同的印象。 二、基础知识 1、Python变量赋值语法非常灵活&#xff01; &#xff08;1&…

基于 ARM+FPGA+AD平台的多类型同步信号采集仪开发及试验验证(一)上位机设计

采集仪上位机设计 本章开发了一款基于 C# 的上位机软件&#xff0c;用于对多类型同步信号采集仪的各项功能 进行操作。从采集仪的数据传输需求出发&#xff0c;上位机利用以太网 UDP 协议实现与采集仪 的数据交互&#xff0c;包括向采集仪发送控制信息与配置信息、接收采…