07 创建型模式-单例模式

1.单例模式介绍

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,此模式保证
某个类在运行期间,只有一个实例对外提供服务,而这个类被称为单例类。

2.使用单例模式要做的两件事
  1. 保证一个类只有一个实例
  2. 为该实例提供一个全局访问节点
3. 单例模式结构

在这里插入图片描述

4.1 单例模式之饿汉式:

在类加载期间初始化静态实例,保证 instance 实例的创建是线程安全的 ( 实例在
类加载时实例化,有JVM保证线程安全).
特点: 不支持延迟加载实例(懒加载) , 此中方式类加载比较慢,但是获取实例对象
比较快。

问题: 该对象足够大的话,而一直没有使用就会造成内存的浪费。

public class Singleton_01 {//1. 私有构造方法private Singleton_01(){}//2. 在本类中创建私有静态的全局对象private static Singleton_01 instance = new Singleton_01();//3. 提供一个全局访问点,供外部获取单例对象public static Singleton_01 getInstance(){return instance;}}
4.2懒汉式(线程不安全)

此种方式的单例实现了懒加载,只有调用getInstance方法时 才创建对象.但是如
果是多线程情况,会出现线程安全问题.

public class Singleton_02 {//1. 私有构造方法private Singleton_02(){}//2. 在本类中创建私有静态的全局对象private static Singleton_02 instance;//3. 通过判断对象是否被初始化,来选择是否创建对象public static Singleton_02 getInstance(){if(instance == null){instance = new Singleton_02();}return instance;}}

注意:
假设在单例类被实例化之前,有两个线程同时在获取单例对象,线程A在执
行完if (instance == null) 后,线程调度机制将 CPU 资源分配给线程B,此
时线程B在执行 if (instance == null)时也发现单例类还没有被实例化,这样
就会导致单例类被实例化两次。为了防止这种情况发生,需要对
getInstance() 方法同步处理。改进后的懒汉模式.

懒汉式(线程安全)

原理: 使用同步锁 synchronized 锁住 创建单例的方法 ,防止多个线程同时调
用,从而避免造成单例被多次创建。

public class Singleton_03 {//1. 私有构造方法private Singleton_03(){}//2. 在本类中创建私有静态的全局对象private static Singleton_03 instance;//3. 通过添加synchronize,保证多线程模式下的单例对象的唯一性public static synchronized Singleton_03 getInstance(){if(instance == null){instance = new Singleton_03();}return instance;}}

缺点
懒汉式的缺点也很明显,我们给 getInstance() 这个方法加了一把大锁
(synchronzed),导致这个函数的并发度很低。量化一下的话,并发度
是 1,也就相当于串行操作了。而这个函数是在单例使用期间,一直会被调
用。如果这个单例类偶尔会被用到,那这种实现方式还可以接受。但是,
如果频繁地用到,那频繁加锁、释放锁及并发度低等问题,会导致性能瓶
颈,这种实现方式就不可取了。

双重校验

饿汉式不支持延迟加载,懒汉式有性能问题,不支持高并发。那我们再来看一
种既支持延迟加载、又支持高并发的单例实现方式,也就是双重检测实现方
式。

实现步骤:

  1. 在声明变量时使用了 volatile 关键字,其作用有两个:
    保证变量的可见性:当一个被volatile关键字修饰的变量被一个线程修改的时
    候,其他线程可以立刻得到修改之后的结果。
    屏蔽指令重排序:指令重排序是编译器和处理器为了高效对程序进行优化的手
    段,它只能保证程序执行的结果时正确的,但 是无法保证程序的操作顺序与代
    码顺序一致。这在单线程中不会构成问题,但是在多线程中就会出现问题。
  2. 将同步方法改为同步代码块. 在同步代码块中使用二次检查,以保证其不被
    重复实例化 同时在调用getInstance()方法时不进行同步锁,效率高。
public class Singleton_04 {//1. 私有构造方法private Singleton_04(){}//2. 使用 volatile保证变量的可见性private volatile static Singleton_04 instance = null;//3. 对外提供静态方法获取对象public static Singleton_04 getInstance(){//第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例if(instance == null){synchronized (Singleton_04.class){//抢到锁之后再次进行判断是否为nullif(instance == null){instance = new Singleton_04();}}}return instance;}}
静态内部类

原理 根据静态内部类 的特性(外部类的加载不影响内部类),同时解决了按
需加载、线程安全的问题,同时实现简洁。

  1. 在静态内部类里创建单例,在装载该内部类时才会去创建单例
  2. 线程安全:类是由 JVM 加载,而 JVM 只会加载1遍,保证只有1个单例
public class Singleton_05 {private static class SingletonHandler{private static Singleton_05 instance = new Singleton_05();}private Singleton_05(){}public static Singleton_05 getInstance(){return SingletonHandler.instance;}}
反射对于单例的破坏

反射技术过于强大,它可以通过 setAccessible() 来修改构造器,字段,方法
的可见性。单例模式的构造方法是私有的,如果将其可见性设为 public ,那么
将无法控制对象的创建。

public class Test_Reflect {public static void main(String[] args) {try {//反射中,欲获取一个类或者调用某个类的方法,首先要获取到该类的Class 对象。Class<Singleton_05> clazz = Singleton_05.class;//getDeclaredXxx: 不受权限控制的获取类的成员.Constructor c = clazz.getDeclaredConstructor(null);//设置为true,就可以对类中的私有成员进行操作了c.setAccessible(true);Object instance1 = c.newInstance();Object instance2 = c.newInstance();System.out.println(instance1 == instance2);} catch (Exception e) {e.printStackTrace();}}}

解决方法之一: 在单例类的构造方法中 添加判断 instance != null 时,直接抛
出异常

public class Singleton_05 {private static class SingletonHandler{private static Singleton_05 instance = new Singleton_05();}private Singleton_05(){if(SingletonHandler.instance != null){throw new RuntimeException("不允许非法访问!");}}public static Singleton_05 getInstance(){return SingletonHandler.instance;}
}

上面的这种方式使代码简洁性遭到破坏,设计不够优雅.

序列化对于单例的破坏
public class Test_Serializable {@Testpublic void test() throws Exception{//序列化对象输出流ObjectOutputStream oos = new ObjectOutputStream(newFileOutputStream("tempFile.obj"));oos.writeObject(Singleton.getInstance());//序列化对象输入流File file = new File("tempFile.obj");ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));Singleton Singleton = (Singleton) ois.readObject();System.out.println(Singleton);System.out.println(Singleton.getInstance());//判断是否是同一个对象System.out.println(Singleton.getInstance() == Singleton);//false}
}class Singleton implements Serializable{private volatile static Singleton singleton;private Singleton() {}public static Singleton getInstance() {if (singleton == null) {synchronized (Singleton.class) {if (singleton == null) {singleton = new Singleton();}}}return singleton;}
}

输出结构为false,说明:
通过对Singleton的序列化与反序列化得到的对象是一个新的对象,这就 破坏了
Singleton的单例性 。

解决方案:

/**
* 解决方案:只要在Singleton类中定义readResolve就可以解决该问题
* 程序会判断是否有readResolve方法,如果存在就在执行该方法,如果不存在-
-就创建一个对象
*/
private Object readResolve() {return singleton;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
readOrdinaryObject方法的代码片段

private Object readOrdinaryObject(boolean unshared)
throws IOException
{
//此处省略部分代码
Object obj;
try {
//通过反射创建的这个obj对象,就是本方法要返回的对象,也
可以暂时理解为是ObjectInputStream的readObject返回的对象。
//isInstantiable:如果一个serializable的类可以在运行
时被实例化,那么该方法就返回true
//desc.newInstance:该方法通过反射的方式调用无参构造方
法新建一个对象。
obj = desc.isInstantiable() ? desc.newInstance()
: null;
} catch (Exception ex) {
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
}
return obj;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5 枚举(推荐方式)

在这里插入图片描述

public enum Singleton_06{INSTANCE;private Object data;public Object getData() {return data;}public void setData(Object data) {this.data = data;}public static Singleton_06 getInstance(){return INSTANCE;}
}

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

【API篇】五、Flink分流合流API

文章目录 1、filter算子实现分流2、分流&#xff1a;使用侧输出流3、合流&#xff1a;union4、合流&#xff1a;connect5、connect案例 分流&#xff0c;很形象的一个词&#xff0c;就像一条大河&#xff0c;遇到岸边有分叉的&#xff0c;而形成了主流和测流。对于数据流也一样…

GitHub-使用 Git工具 创建密钥id_rsa.pub

快速导航 步骤1 打开Git Bash步骤2 输入指令【ssh-keygen】步骤3 打开创建的公钥文件步骤4 复制其中所有内容步骤5 打开GitHub中的Setting界面步骤6 添加SSH keys 步骤1 打开Git Bash 打开Git Bash 工具 步骤2 输入指令【ssh-keygen】 输入指令【ssh-keygen】&#xff0c;并…

【四:httpclient的使用】

目录 1、Demo案例2、请求一个带cookies的get请求3、请求一个带cookies的post请求案例一&#xff0c;案例二的properties的配置 1、Demo案例 public class MyHttpClient {Testpublic void test1() throws IOException {//用来存放我们的结果String result;HttpGet get new Htt…

安装VSCode,提升工作效率!iPad Pro生产力进阶之路

文章目录 前言1. 本地环境配置2. 内网穿透2.1 安装cpolar内网穿透(支持一键自动安装脚本)2.2 创建HTTP隧道 3. 测试远程访问4. 配置固定二级子域名4.1 保留二级子域名4.2 配置二级子域名 5. 测试使用固定二级子域名远程访问6. iPad通过软件远程vscode6.1 创建TCP隧道 7. ipad远…

模型量化笔记--KL散度量化

KL散度量化 前面介绍的非对称量化中&#xff0c;是将数据中的min值和max值直接映射到[-128, 127]。 同样的&#xff0c;前面介绍的对称量化是将数据的最大绝对值 ∣ m a x ∣ |max| ∣max∣直接映射到127。 上面两种直接映射的方法比较粗暴&#xff0c;而TensorRT中的int8量化…

openGauss学习笔记-102 openGauss 数据库管理-管理数据库安全-客户端接入之查看数据库连接数

文章目录 openGauss学习笔记-102 openGauss 数据库管理-管理数据库安全-客户端接入之查看数据库连接数102.1 背景信息102.2 操作步骤 openGauss学习笔记-102 openGauss 数据库管理-管理数据库安全-客户端接入之查看数据库连接数 102.1 背景信息 当用户连接数达到上限后&#…

小黑子—Maven基础

Maven基础 一 小黑子的Maven学习1. Mavn的介绍2. Maven基础概念2.1 仓库2.2 坐标2.3 仓库配置 3. 手动写一个maven项目3.1 Maven项目构建命令3.2 插件创建工程 4. IDEA下的maven项目5. 依赖管理5.1 依赖配置5.2 依赖传递5.3 可选依赖&#xff08;不透明&#xff09;5.4 排除依赖…

【一:实战开发testng的介绍】

目录 1、主要内容1.1、为啥要做接口测试1.2、接口自动化测试落地过程1.3、接口测试范围1.4、手工接口常用的工具1.5、自动化框架的设计 2、testng自动化测试框架基本测试1、基本注解2、忽略测试3、依赖测试4、超时测试5、异常测试6、通过xml文件参数测试7、通过data实现数据驱动…

UWB十个知识点

UWB是一直被基于厚望的高精度定位技术 1&#xff1a;定位技术及UWB特点 位置空间感知技术包括了GNSS、RFID、蓝牙和UWB&#xff0c;在室内和区域空间测量最具技术优势的技术是UWB。 GNSS是广域定位技术&#xff0c;室内以及建筑物旁边等场景&#xff0c;GNSS无法实现定位&am…

【微服务 SpringCloud】实用篇 · Ribbon负载均衡

微服务&#xff08;4&#xff09; 文章目录 微服务&#xff08;4&#xff09;1. 负载均衡原理2. 源码跟踪1&#xff09;LoadBalancerIntercepor2&#xff09;LoadBalancerClient3&#xff09;负载均衡策略IRule4&#xff09;总结 3. 负载均衡策略3.1 负载均衡策略3.2 自定义负载…

企业IT资产设备折旧残值如何计算

环境&#xff1a; 企业/公司 IT资产 问题描述&#xff1a; 企业IT设备折旧残值如何计算&#xff1f; 解决方案&#xff1a; 1.按三年折旧 净值原值-月折旧额折旧月份 &#xff0c; 月折旧额原值(1-3%)/36 折旧月份ROUND(E2*(1-3%)/36,2) 2.净值E2-F2*G2

vue使用pdf 导出当前页面,(jspdf, html2canvas )

需要安装两个插件 npm install html2canvas jspdfyarn add html2canvas jspdf<div class"app-container" id"pdfPage">我是内容 </div><el-button size"mini" click"onExportPdf">导出数据</el-button>onexp…

代码随想录算法训练营第23期day24|回溯算法理论基础、77. 组合

目录 一、回溯算法基础 回溯法模板 二、&#xff08;leetcode 77&#xff09;组合 剪枝 一、回溯算法基础 1.回溯的本质是穷举&#xff0c;穷举所有可能&#xff0c;然后选出想要的答案&#xff08;为了提升效率&#xff0c;最多再加一个剪枝&#xff09; 2.回溯法解决的…

凝聚技术力量 共建测试生态 ——集成电路测试技术交流日成功举办

10月18日下午&#xff0c;凝聚技术力量&#xff0c;共建测试生态 ——集成电路测试技术交流会在上海成功举办。来自全国各地知名专家学者、技术大咖及企业代表齐聚一堂&#xff0c;共同探讨封装测试技术的发展方向&#xff0c;共话产业未来&#xff0c;共促产业发展。 本次活动…

Stable Diffusion WebUI扩展a1111-sd-webui-tagcomplete之Booru风格Tag自动补全功能详细介绍

安装地址 直接附上地址先: Ranting8323 / A1111 Sd Webui Tagcomplete GitCodeGitCode——开源代码托管平台,独立第三方开源社区,Git/Github/Gitlabhttps://gitcode.net/ranting8323/a1111-sd-webui-tagcomplete.git上面是GitCode的地址,下面是GitHub的地址,根据自身情…

CUDA编程入门系列(二) GPU硬件架构综述

一、Fermi GPU Fermi GPU如下图所示&#xff0c;由16个SM&#xff08;stream multiprocessor&#xff09;组成&#xff0c;不同的SM之间通过L2 Cache和全局内存进行相连。整个架构大致分为两个层次&#xff0c;①总体架构由多个SM组成 ②每个SM由多个SP core&#xff08;stream…

数据结构中的七大排序(Java实现)

目录 一、直接插入排序 二、希尔排序 三、直接选择排序 四、堆排序 五、冒泡排序 六、快速排序 七、归并排序 一、直接插入排序 思想&#xff1a; 定义i下标之前的元素全部已经有序&#xff0c;遍历一遍要排序的数组&#xff0c;把i下标前的元素全部进行排序&#xff0…

elementui select组件下拉框底部增加自定义按钮

elementui select组件下拉框底部增加自定义按钮 el-select组件的visible-change 事件&#xff08;下拉框出现/隐藏时触发&#xff09; <el-selectref"select":value"value"placeholder"请选择"visible-change"visibleChange">&…

一天吃透Java集合面试八股文

内容摘自我的学习网站&#xff1a;topjavaer.cn 常见的集合有哪些&#xff1f; Java集合类主要由两个接口Collection和Map派生出来的&#xff0c;Collection有三个子接口&#xff1a;List、Set、Queue。 Java集合框架图如下&#xff1a; List代表了有序可重复集合&#xff0c…

软考-访问控制技术原理与应用

本文为作者学习文章&#xff0c;按作者习惯写成&#xff0c;如有错误或需要追加内容请留言&#xff08;不喜勿喷&#xff09; 本文为追加文章&#xff0c;后期慢慢追加 by 2023年10月 访问控制概念 访问控制是计算机安全的一个重要组成部分&#xff0c;用于控制用户或程序如…