深度学习设计模式之单例模式

一、单例模式简介

一个类只能有一个实例,提供该实例的全局访问点;

二、单例模式实现步骤

使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。
私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。
在这里插入图片描述

三、单例模式的两种方式

在这里插入图片描述

1.懒汉模式

懒汉模式,通俗来讲就是只有饿的时候,才会去找饭吃。通常只有对象被需要的时候才会去创建。最显而易见的优点就是,节省资源。如果没有地方用到这个类,这个类将不会进行实例化。

1.1 简易版懒汉模式

public class LzaySingleton {/*** 成员变量*/private static LzaySingleton lzaySingleton;/*** 构造方法私有化*/private LzaySingleton(){}/*** 获取单例对象* @return*/public static LzaySingleton  getInstance(){if(lzaySingleton == null){System.out.println("创建实例");lzaySingleton =  new LzaySingleton();return lzaySingleton;}System.out.println("实例对象已存在,无需再创建");return lzaySingleton;}}

测试类

    public static void main(String[] args) {// 先创建一个对象,看是否有输出LzaySingleton lzaySingleton = LzaySingleton.getInstance();LzaySingleton lzaySingleton1 = LzaySingleton.getInstance();}

结果:
在这里插入图片描述
简易版的单例模式存在的问题就是:在多线程的情况下是不安全的,会打破单例的定义。
例如:有2个线程,线程A,线程B;同时成员变量lzaySingleton为null;线程A,线程B,同时走到if(lzaySingleton == null),那将会执行两次lzaySingleton = new LzaySingleton();就会实例化两次对象,从而打破单例模式的设定。
在这里插入图片描述
怎么解决呢?接下来就是我们另外一种懒汉单例模式登场了。

1.2线程安全的单例模式

怎么解决线程安全?那就很简单了,加锁就可以了。
只需要再getInstance()方法上加 synchronized就行,这样保证同一个时间点,只会有一个线程进入到这个方法,从而解决多次创建实例的问题。

public class LzaySingleton {/*** 成员变量*/private static LzaySingleton lzaySingleton;/*** 构造方法私有化*/private LzaySingleton(){}/*** 获取单例对象* @return*/public static synchronized LzaySingleton  getInstance(){if(lzaySingleton == null){System.out.println("创建实例");lzaySingleton =  new LzaySingleton();return lzaySingleton;}System.out.println("实例对象已存在,无需再创建");return lzaySingleton;}}

以上的方法虽然可以解决多线程的问题,但是往往单例对象的内容逻辑是非常复杂的,使用synchronized 修饰方法,当其他线程进入该方法的时候,就会进入等待,对性能还是有一定影响的。
解决这个问题,可以灵活的使用synchronized

1.3 线程安全的单例模式V2.0版

为了解决synchronized修饰方法带来的系统开销。我们可以通过灵活运用synchronized来解决此问题。众所周知synchronized 加锁是有多种方式的。我们使用代码块的方式,只有再创建对象的时候使用 synchronized

public class LzaySingleton {/*** 成员变量*/private static LzaySingleton lzaySingleton;/*** 构造方法私有化*/private LzaySingleton(){}/*** 获取单例对象* @return*/public static LzaySingleton  getInstance(){if(lzaySingleton == null){//  synchronized 代码块synchronized (LzaySingleton.class){System.out.println("创建实例");lzaySingleton =  new LzaySingleton();}return lzaySingleton;}System.out.println("实例对象已存在,无需再创建");return lzaySingleton;}}

这种方式虽然解决了,锁粒度问题带来的性能开销问题,但是又有一个致命问题,我们又回到解放前了。
同样的多线程问题,如果线程A,线程B,同时又到了这一步:
在这里插入图片描述
线程A和B拿到的对象都是null,然后线程A侥幸拿到了锁,线程B就只能再外面等待线程A。同样的问题就会再现,线程A执行完lzaySingleton = new LzaySingleton();线程B就会拿到锁,然后再执行一次lzaySingleton = new LzaySingleton();,所有使用synchronized 代码块的方式加锁,还不够完善。

1.3 线程安全的单例模式V2.1版-双重校验锁

因为上面使用了synchronized 代码块的方式加锁,减少了系统的开销,但是也带来了新的问题,因此我们多增加一个判断,如下:

public class LzaySingleton {/*** 成员变量*/private static LzaySingleton lzaySingleton;/*** 构造方法私有化*/private LzaySingleton(){}/*** 获取单例对象* @return*/public static LzaySingleton  getInstance(){if(lzaySingleton == null){synchronized (LzaySingleton.class){if(lzaySingleton == null){System.out.println("创建实例");lzaySingleton =  new LzaySingleton();}}return lzaySingleton;}System.out.println("实例对象已存在,无需再创建");return lzaySingleton;}}

这样,即使线程A和线程B同时都到了这一步:
在这里插入图片描述
即使A拿到了锁,执行完lzaySingleton = new LzaySingleton();以后,到B执行时也会被这个校验给拦住
在这里插入图片描述
至此高性能加锁的单例模式完成,但是他还不是最终版本,依旧存在一些小问题。

1.3 线程安全的单例模式V3.0版-双重校验锁终极版本

目前代码层面已经解决问题,但是深究底层,时 lzaySingleton = new LzaySingleton();这个操作并不是原子性的,因为底层在编译运行代码的时候,会对当前代码进行优化,会存在指令重排序情况。而 new LzaySingleton() 时至少需要3步才能完成。
1.分配内存空间;
2.实例化对象;
3.将对象指向分配的空间地址;
如果编译的时候进行了指令重排序,本来正常操作时 1 -> 2 -> 3这样,重排序后则可能会出现 1-> 3 -> 2 这个时候,单线程肯定没问题,但是在多线程的情况下,因为对象还没创建完成,其他线程执行到这里的时候,认为对象不为空,已经实例化成功了,就直接获取对象使用了。其实拿到的对象并不是最终的对象,只是一个半成品的,所以使用的过程中,就会出现意想不到的问题。
在这里插入图片描述
这个时候就需要使用 JVM的关键字 volatile 来解决指令重排序的问题了。
简单介绍一下 volatile
1.volatile有3个特性:可见性、有序性、原子性;
可见性是当多个线程同时访问一个变量的时候,其中一个线程修改了变量的值,其他线程能立刻看到修改的变量值。
有序性是禁止了指令重排序,执行程序代码时,按照顺序来执行。
原子性是一个操作是不能中断的,要不全部都执行,要不都不执行。
2. volatile 是用来修饰变量的,无法修饰代码块和方法。
3. volatile的使用:只要修饰一个 可能被多线程同时访问的变量上就行。
详细情况可自行查询相关资料。

最终代码如下: 对成员变量lzaySingleton 进行了volatile 修饰,防止了创建时的指令重排序。

public class LzaySingleton {/*** 成员变量*/private static volatile LzaySingleton lzaySingleton;/*** 构造方法私有化*/private LzaySingleton(){}/*** 获取单例对象* @return*/public static LzaySingleton  getInstance(){if(lzaySingleton == null){synchronized (LzaySingleton.class){if(lzaySingleton == null){System.out.println("创建实例");lzaySingleton =  new LzaySingleton();}}return lzaySingleton;}System.out.println("实例对象已存在,无需再创建");return lzaySingleton;}}

至此单例模式的懒汉模式最终版完成。

2.饿汉模式

饿汉模式相对来说,比较简单,通俗来说就是,一上来就先去找吃的和懒汉相反。系统加载的时候就初始化对象。优点就是简单,不存在什么多线程问题。缺点就是占用内存。
实现如下:

public class HungrySingleton {// 一开始就初始化对象private static HungrySingleton hungrySingleton = new HungrySingleton();// 私有化构造方法private HungrySingleton(){}public static HungrySingleton getInstance(){return hungrySingleton;}
}

四、扩展实现单例

1.使用枚举的方式实现单例模式

使用枚举的方式实现单例模式,是《Effective java》一书中提到的
在这里插入图片描述
上面的几种方式已经实现了单例模式,但是如果碰到特殊的情况,比如反射的时候,通过 setAccessible() 方法还是可以将私有构造函数的访问级别设置为 public,然后调用构造函数从而实例化对象的。使用枚举就天然的解决反射问题。

直接在枚举类里面写功能方法,代码如下:

public enum SingletonEnum {INSTANCE;public void test(){System.out.println("1111");}
}

测试类

   public static void main(String[] args) {SingletonEnum.INSTANCE.test();}

结果
在这里插入图片描述

2.使用内部类的方式实现单例模式

内部类的方式实现单例模式,加载Singleton的时候静态内部类 SingletonHolder 不会被加载。 只有调用 getInstance()方法的时候才会去初始化对象。这种方式不仅具有延迟初始化的好处,而且由虚拟机提供了对线程安全的支持。
以下是代码实现:

/*** 静态内部类方式实现单例*/
public class Singleton {// 私有化构造方法private Singleton(){}public void test(){System.out.println("2222");}/*** 静态内部类*/private static class SingletonHolder{// 初始化对象private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance(){return SingletonHolder.INSTANCE;}}

测试类

    public static void main(String[] args) {Singleton.getInstance().test();}

结果
在这里插入图片描述

FAQ

1.为什么要私有化构造方法?

单例模式主要特点就是保证对象只被实例化一次,所以构造方法的私有化,才能保证不能随便的去new() 对象,从而保证对象只能初始化一次。

2.为什么成员变量要用 static 修饰?

程序调用类中方法只有两种方式,①创建类的一个对象,用该对象去调用类中方法;②使用类名直接调用类中方法,格式“类名.方法名()”;
现在没有办法new 对象了,所以只能使用第二种方式。
java中静态方法没有办法调用非静态的类或者变量,所以成员变量也需要使用static来修饰。

3.单例模式的应用场景?

  • 数据库连接池:数据库连接池是一个重要的资源,单例模式可以确保应用程序中只有一个数据库连接池实例,避免资源浪费。
  • 配置文件管理器:应用程序通常需要一个配置文件管理器来管理配置文件,单例模式可以确保在整个应用程序中只有一个这样的实例。
  • 缓存系统:缓存系统是提高应用程序性能的重要组件,单例模式可以确保只有一个缓存实例。

4.单例模式使用的注意情况

单例模式主要分为 懒汉 和饿汉,我们通常再使用的时候要综合评估两种方式的优缺点,决定使用,比如:对于一些占用内存小的类我们使用饿汉模式,占用内存较大的类我们就使用懒汉模式。一开始就需要加载的并且会被频繁使用的就用饿汉模式。

5.JDK中的单例

java.lang.Runtime类使用的就是单例模式(饿汉),这个类是运行时的类,很多信息需要获取所以使用的是饿汉单例模式,如下:
在这里插入图片描述
java.awt.Desktop类使用的是懒汉单例模式:
在这里插入图片描述

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

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

相关文章

【OpenGauss源码学习 —— (ALTER TABLE(ExecRewriteCStoreTable))】

ALTER TABLE(ExecRewriteCStoreTable) 概述ExecRewriteCStoreTable 函数ATCStoreRewriteTable 函数ATCStoreGetRewriteAttrs 函数ChangeTableSpaceForDeltaRelation 函数ATOnlyCheckCStoreTable 函数 声明:本文的部分内容参考了他人的文章。在…

JavaScript与Nest.js:打造高性能的服务器端应用

JavaScript与Nest.js:打造高性能的服务器端应用 在现代Web开发的广阔天地里,JavaScript已经不再局限于浏览器的疆域,而是凭借Node.js的强大力量,向服务器端领域大步迈进。Nest.js,一个基于Node.js的渐进式框架&#x…

【计算机网络】物理层 通信基础、奈氏准则、香农公式 习题2

下列说法中正确的是( )。 A. 信道与通信电路类似,一条可通信的电路往往包含一个信道 B.调制是指把模拟数据转换为数字信号的过程 C. 信息传输速率是指通信信道上每秒传输的码元数 D.在数值上,波特率等于比特率与每符号所含的比特数的比值 信息传输速率&a…

65-CPLD电路设计(安路为例)

视频链接 CPLD电路设计(安路为例)01_哔哩哔哩_bilibili CPLD电路设计(以安路为例) 浅谈板级电源设计的三种方法_哔哩哔哩_bilibili 参考【浅谈板级电源设计的三种方法】 FPGA板级硬件实战S1~7课 实战Power2-电…

一次基类类型对象无法被传递问题的分析

看下面一段代码&#xff1a; // proj2.cpp #include <iostream> using namespace std; class CharShape { public:CharShape(char ch) : _ch(ch) {};virtual void Show() 0; protected:char _ch; // 组成图形的字符 }; class Triangle : public CharShape { public:Tr…

^_^填坑备忘^_^C#自动化编程实现STK+Exata对卫星互联网星座进行网络仿真

C#实际选择 STK11版本 or STK12版本的问题备注。 【C#自动化客户端调用STK时&#xff0c;实际选择 STK11版本 or STK12版本 的调试运行备注】 以下代码“更新并重新打包备份为”〔testSTKQualNetInterface备份08.1_★避坑★【种子卫星&#xff1a;天线直接安装在卫星上&#…

自贡在线教育系统报价,报班、网课还是自学怎么选择呢?

现在的教育模式已经不局限于传统的教育模式了&#xff0c;教育模式已经有很多模式&#xff0c;有各种辅导班、网课课程&#xff0c;很多人都在想报班好还是选网课&#xff0c;或者自学呢&#xff1f; 自学&#xff1a;成本最低&#xff0c;效率因人而异。只需要花上百把块钱买教…

1.下午试题1

1.15分 拿到10分之上 前三个问 12分 最后一个3分 前三个都是固定的题目 2.写出实体名称 写出数据存储问题 补充数据流起点与终点 3.数据流图 也称为数据流程图 Data Flow Diagram DFD 4.分为外部实体Entity 加工Process 数据存储Data Store 数据流 5.外部实体 当前系统之外的 人…

centos7.9系统安全加固

1、限制用户登陆 vim /etc/hosts.deny&#xff0c;若禁止192.168.0.158对服务器进行ssh的登陆&#xff0c;添加如下内容 sshd : 192.168.0.158 添加完毕后就生效了&#xff0c;直接用192.168.0.158访问主机&#xff0c;就无法连接了&#xff0c;显示 Connection closing...Soc…

系统需求开发和管理指南(软件标准文件Word)

1.需求获取的方式 2.需求分析的准则 3.需求分析的方法 4.需求开发考虑的方面 5.需求确认的方法 6.需求优先级的设定 7.需求文档编制规范要求 软件全文档获取方式一&#xff1a;本文末个人名片直接获取。 软件全文档获取二&#xff1a;软件项目开发全套文档下载_软件项目文档-C…

相机3:曝光三要素之光圈与快门

背景介绍 曝光三要素&#xff1a;光圈&#xff0c;快门&#xff0c;感光度 光圈&#xff1a; 光圈数值越小&#xff0c;画面清晰范围越少&#xff0c;画面背景虚化越明显&#xff1b; 光圈数值越大&#xff0c;画面清晰范围越多&#xff0c;背景虚化越不明显。 快门&#xff1a…

Xilinx FPGA底层逻辑资源简介(1):关于LC,CLB,SLICE,LUT,FF的概念

LC&#xff1a;Logic Cell 逻辑单元 Logic Cell是Xilinx定义的一种标准&#xff0c;用于定义不同系列器件的大小。对于7系列芯片&#xff0c;通常在名字中就已经体现了LC的大小&#xff0c;在UG474中原话为&#xff1a; 对于7a75t芯片&#xff0c;LC的大小为75K&#xff0c;6输…

VPN方案和特点

VPN方案和特点 VPN&#xff0c;或者称为虚拟专用网络&#xff0c;是一种保护你的在线安全和隐私的技术。它可以创建一个加密的连接&#xff0c;使你的在线活动对其他人不可见。以下是一些常见的VPN协议和它们的特点&#xff1a; 开放VPN (OpenVPN)&#xff1a;这是一种极为可…

利用Spring Boot与Redisson实时排行榜功能

在现代的Web应用程序中&#xff0c;排行榜功能是常见且重要的需求之一。它可以用于展示热门内容、评选优秀用户等场景。 本文将介绍如何利用Spring Boot和Redisson实现排行榜功能&#xff0c;让你的应用程序更具吸引力和竞争力。 1. 概述 排行榜功能通常涉及到大量的数据计算…

阿里云和AWS负载均衡服务对比分析

在云计算时代,负载均衡作为一种关键的网络基础设施,承担着在多个服务器之间分发网络流量的重要任务。作为全球两大主要的云服务提供商,阿里云和Amazon Web Services(AWS)都提供了强大的负载均衡解决方案。本文将从性能、功能、可用性和成本等方面对两者进行对比分析。我们九河云…

600/天,海外项目值班,接不接?

朋友介绍了一个海外项目&#xff0c;广告系统短期维护&#xff0c;刚上线需要维护14天也就是2个星期&#xff0c;费用单价600/天&#xff0c;主要工作内容&#xff1a;北京晚上12点-早上8点值班&#xff0c;如果有问题及时响应并修复。 如果我年轻10岁&#xff0c;这个项目我倒…

【牛客】SQL202 找出所有员工当前薪水salary情况

1、描述 有一个薪水表&#xff0c;salaries简况如下&#xff1a; 请你找出所有员工具体的薪水salary情况&#xff0c;对于相同的薪水只显示一次,并按照逆序显示&#xff0c;以上例子输出如下&#xff1a; 2、题目建表 drop table if exists salaries ; CREATE TABLE sala…

【全开源】Java知识付费教育付费资源付费平台公众号小程序源码

特色功能&#xff1a; 多样化的内容呈现&#xff1a;资源付费平台小程序支持图文、音视频、直播等多种形式的内容呈现&#xff0c;为用户提供了丰富的学习体验。直播课程&#xff1a;专家或讲师可以通过小程序进行在线授课&#xff0c;与用户实时互动&#xff0c;增强了学习的…

【Linux】AlmaLinux 9.4版本发布

AlmaLinux 9.4 正式版发布&#xff0c;该版本基于 Redhat Enterprise 9.4&#xff0c;内核版本号&#xff1a; 5.14.0-427.13.1.el9_4.x86_64 相对于Rocky Linux&#xff0c; AlmaLinux更加的稳定&#xff0c;生产环境建议使用AlmaLinux来替代CentOS 7.x AlmaLinux 9.4版本系统…

【多电压流程 Multivoltage Flow】- 5.特定工具使用建议(1.VCS NLP VC LP)

本章提供了关于使用Synopsys工具进行低功耗设计和分析的信息。它包含以下部分: • 使用VCS NLP和VC LP进行多电压验证 • 使用Design Compiler进行逻辑综合 • 使用IC Compiler进行设计规划 • 使用IC Compiler进行物理实现 • 使用IC Compiler II和Fusion Compiler进行物…