当前位置: 首页 > news >正文

单例模式和工厂模式

目录

今日良言:关关难过关关过,步步难行步步行

一、单例模式

1.饿汉模式

2.懒汉模式

二、工厂模式


今日良言:关关难过关关过,步步难行步步行

 

一、单例模式

首先来解释一下,什么是单例模式。

单例模式也就是单个实例(对象)。在有些场景中,只能创建出一个实例,不应该创建多个实例。

单例模式,就是针对上述的需求场景进行了个更强制的保证,通过巧用 java 现有的语法,达成了某个类 只能被创建出一个实例,这样的效果.(当程序猿不小心创建了多个实例,就会编译报错)。

单例模式最常见的两种就是:饿汉模式和懒汉模式。

1.饿汉模式

代码如下:

class Singleton{// 在此处就把实例给创建出来了private static Singleton instance = new Singleton();// 如果需要使用这个唯一的实例,统一通过这个方法获取public static Singleton getInstance() {return instance;}// 为了避免Singleton 类不小心被赋值出多份,将构造方法设置成private.// 此时就无法通过new 的方式来创建这个Singleton 实例了。private Singleton(){}
}
public class Exercise {public static void main(String[] args)  {Singleton s1 = Singleton.getInstance();Singleton s2 = Singleton.getInstance();System.out.println(s1 == s2);}
}

 

在类加载阶段就将实例创建好了,这种效果就给人一种“特别急切”的感觉。

被static修饰表示这个属性和实例无关,而是和类相关。

Java 代码中的每个类,都会在编译完成后得到.class 文件,JVM 运行时就会加载这个.class文件,读取其中的二进制指令,并且在内存中构造出对应过的类对象(形如:Singleton.class),具体的类加载可以阅读博主之前写的博客:

深度剖析JVM三个面试常考知识点_程序猿小马的博客-CSDN博客

由于 类对象在一个 Java 进程中是唯一的,因此这个类对象的内部的类属性也是唯一的。

static 在这里的作用有两个:

1)static 保证这个实例唯一。

2)static 保证这个实例确实在一定的时机被创建出来。

static 属于这个实现方式中的灵魂角色。

2.懒汉模式

代码实现:

class SingletonLazy {private static SingletonLazy instance = null;public static SingletonLazy getInstance() {if (instance == null) {instance = new SingletonLazy();}return instance;}// 构造方法设置成私有的private SingletonLazy(){}
}
public class Exercise {public static void main(String[] args)  {SingletonLazy s1 = SingletonLazy.getInstance();SingletonLazy s2 = SingletonLazy.getInstance();System.out.println(s1 == s2);}
}

这个实例并非是类加载阶段创建,而是真正第一次使用的时候才去创建, 如果不用就不创建了。

上述写的饿汉模式和懒汉模式是在单线程情况下的代码,如果在多线程下调用getInstance 是否是线程安全的呢?

答案是:一个是线程安全的,一个是线程不安全的。

饿汉模式是线程安全的,因为饿汉模式的 getInstance 方法只涉及到“读操作”。

懒汉模式是线程不安全的,因为懒汉模式的 getInstance 方法既有读操作又有写操作。

线程安全问题的详细解释,博主在之前的博客中有提到:

线程安全问题_程序猿小马的博客-CSDN博客

这里如果在多线程情况下调用懒汉模式的 getInstance 方法,会发生多次 new 操作,显然就不是单例了。

那么,如何让上述懒汉模式能够成为线程安全的呢?

加锁!!!

上述线程安全问题本质上是 修改操作不是原子的,因此,需要保证这个修改操作是原子的。

修改代码如下:

 此时,把锁加到外面,保证了读操作和修改操作是一个整体。

但是,代码写到这里,还有问题,上述这种写法,就导致了每次 getInstance 都需要加锁,加锁操作都是有开销的,仔细考虑一下,这里真的需要每次都加锁吗?

显然不是,这里的加锁只是在new出对象之前加上是有必要的,一旦对象 new 完以后,后续调用 getInstance ,此时 instance 一定是非空的,因此会直接 return。

基于上述讨论,就可以给刚才的代码加上一个判定:

如果对象还没创建才加锁,如果对象已经创建过了,就不加锁了。

修改代码如下:

此时,这里就不再是无脑加锁了,而是满足了特定条件之后,才真正加锁。 

注意:

这两个if 的作用不一样,第一个if 判断是否要加锁,第二个if 判断是否要创建对象。

加锁操作可能会引起线程阻塞,当执行到锁结束之后,执行到第二个 if 的时候,第二个 if 和第一个 if 之间可能已经隔了很久的时间了,instance 变量可能已经被别的线程给修改过了,所以需要第二次 if 判断当前线程是否需要创建对象。

上述代码其实还存在问题: 内存可见性问题以及指令重排序

关于这个问题,博主之前的博客也有详细介绍:

线程安全问题_程序猿小马的博客-CSDN博客 

内存可见性问题:

假设有很多线程都去进行 getInstance ,这个时候,可能会存在被优化的风险(只有第一次读的时候,才真正的读了内存,后续都是读寄存器)

指令重排序:

instance = new SingletonLazy();  

这个操作可以拆分为三个步骤:

1)申请内存空间。

2)调用构造方法,把这个内存空间初始化成一个合理的对象。

3)把内存空间的地址赋值给 instance 引用。

正常情况下,是按照 1 2 3 这个顺序执行代码,但是编译器存在指令重排序问题,编译器为了提高程序效率,会调整代码执行顺序, 1 2 3 可能就变成了 1 3  2 ,如果是单线程下,1 2 3 和 1 3 2 没有本质区别,但是多线程下就会出现问题了。 

假设线程 t1 是 按照 1 3 2 的步骤执行的,t1 执行到 1 3 之后,执行 2 之前,被切除 cpu ,此时 t2执行,(当 t1 执行完 3 之后,t2 看起来此处的引用就非空了)此时此刻,t2 就相当于直接返回了 instance 引用,并且可能会尝试使用引用中的属性,但是由于 t1 中的 2 操作还没执行完呢,t2 拿到的是非法的对象,还没构造完成的不完整的对象。

因此,需要解决上述问题,使用 volatile  !!!

修改代码如下:

 懒汉模式完整代码如下:

class SingletonLazy {private volatile static SingletonLazy instance = null;public static SingletonLazy getInstance() {if (instance == null) {synchronized (SingletonLazy.class) {if (instance == null) {instance = new SingletonLazy();}}}return instance;}// 构造方法设置成私有的private SingletonLazy(){}
}
public class Exercise {public static void main(String[] args)  {SingletonLazy s1 = SingletonLazy.getInstance();SingletonLazy s2 = SingletonLazy.getInstance();System.out.println(s1 == s2);}
}

二、工厂模式

先来解释一下什么是工厂模式。

工程模式用一句话概括:使用普通的方法,来代替构造方法创建对象。

为什么要代替呢? 这是因为构造方法有坑,坑就体现在,如果只构造一种对象,好办,如果要构造多种不同的情况,就不好办了。

举个例子:

假设现在有一个类,表示平面上的一个点

 上述构造方法表示使用笛卡尔坐标系提供的坐标来构造点。

如果这里假设再使用极坐标来构造点,代码如下:

很明显,这个代码存在问题,正常来说,多个构造方法,是通过“重载”的方式来提供的,重载要求的是 方法名相同,参数的个数或者类型不同。

 

为了解决这个问题,就可以使用工厂模式:

 普通方法的方法名没有限制,因此有多种方式构造,使用不同的方法名即可。

以上就是单例模式和工厂模式的介绍。

 

相关文章:

单例模式和工厂模式

目录 今日良言:关关难过关关过,步步难行步步行 一、单例模式 1.饿汉模式 2.懒汉模式 二、工厂模式 今日良言:关关难过关关过,步步难行步步行 一、单例模式 首先来解释一下,什么是单例模式。 单例模式也就是单个…...

两个镜头、视野、分辨率不同的相机(rgb、红外)的视野校正

文章目录 背景实际效果查找资料资料1资料2 解决方案最终结果 背景 目前在做的项目用到两个摄像头,一个是热成像摄像头、另一个是普通的rgb摄像头。 一开始的目标是让他们像素级重合,使得点击rgb图像时,即可知道其像素对应的温度。但是在尝试…...

kettle 连接jdbc

DM JDBC连接 oracle JDBC连接 PG JDBC连接 SQLSERVER JDBC连接...

PyTorch中加载模型权重 A匹配B|A不匹配B

在做深度学习项目时,从头训练一个模型是需要大量时间和算力的,我们通常采用加载预训练权重的方法,而我们往往面临以下几种情况: 未修改网络,A与B一致 很简单,直接.load_state_dict() net ANet(num_cla…...

@FeignClient指定多个url实现负载均衡

C知道回答的如下: 在使用 FeignClient 调用多个 URL 实现负载均衡时,可以使用 Spring Cloud Ribbon 提供的功能来实现。下面是一个示例代码: 首先,在Spring Boot主类上添加EnableFeignClients注解启用Feign Client功能。 Spring…...

vue diff 双端比较算法

文章目录 双端指针比较策略命中策略四命中策略二命中策略三命中策略一未命中四种策略,遍历旧节点列表新增情况一新增情况二 删除节点双端比较的优势 双端指针 使用四个变量 oldStartIdx、oldEndIdx、newStartIdx 以及 newEndIdx 分别存储旧 children 和新 children …...

初识React: 基础(概念 特点 高效原因 虚拟DOM JSX语法 组件)

1.什么是React? React是一个由Facebook开源的JavaScript库,它主要用于构建用户界面。React的特点是使用组件化的思想来构建界面,使得代码的可复用性和可维护性大大提高。React还引入了虚拟DOM的概念,减少了对真实DOM的直接操作,…...

自监督去噪:Neighbor2Neighbor原理分析与总结

文章目录 1. 方法原理1.1 先前方法总结1.2 Noise2Noise回顾1.3 从Noise2Noise到Neighbor2Neighbor1.4 框架结构2. 实验结果3. 总结 文章链接:https://arxiv.org/abs/2101.02824 参考博客:https://arxiv.org/abs/2101.02824 1. 方法原理 1.1 先前方法总…...

简单工厂模式(Simple Factory)

简单工厂模式,又称为静态工厂方法(Static Factory Method)模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。简单工厂模式不属于GoF的23个…...

Agent:OpenAI的下一步,亚马逊云科技站在第5层

什么是Agent?在大模型语境下,可以理解成能自主理解、规划、执行复杂任务的系统。Agent也将成为新的起点,成为各行各业构建新一代AI应用必不可少的组成部分。 对此,初创公司Seednapse AI创始人提出构建AI应用的五层基石理论&#…...

JMeter 4.x 简单使用

文章目录 前言JMeter 4.x 简单使用1. 启动2. 设置成中文3. 接口测试3.1. 设置线程组3.2. HTTP信息请求头管理器3.3. 添加HTTP请求默认值3.4. 添加HTTP cookie 管理3.5. 添加http请求3.5.1. 添加断言 3.6. 添加监听器-查看结果树3.7. 添加监听器-聚合报告 4. 测试 前言 如果您觉…...

深入NLTK:Python自然语言处理库高级教程

在前面的初级和中级教程中,我们了解了NLTK库中的基本和进阶功能,如词干提取、词形还原、n-gram模型和词云的绘制等。在本篇高级教程中,我们将深入探索NLTK的更多高级功能,包括句法解析、命名实体识别、情感分析以及文本分类。 一…...

React 用来解析html 标签的方法

在React中,解析HTML标签通常是使用JSX(JavaScript XML)语法的一部分。JSX允许您在JavaScript代码中编写类似HTML的标记,然后通过React进行解析和渲染。 以下是React中解析HTML标签的几种常见方式: 直接在JSX中使用标…...

【C++】做一个飞机空战小游戏(五)——getch()控制两个飞机图标移动(控制光标位置)

[导读]本系列博文内容链接如下: 【C】做一个飞机空战小游戏(一)——使用getch()函数获得键盘码值 【C】做一个飞机空战小游戏(二)——利用getch()函数实现键盘控制单个字符移动【C】做一个飞机空战小游戏(三)——getch()函数控制任意造型飞机图标移动 【C】做一个飞…...

Flask 是什么?Flask框架详解及实践指南

Flask 是一个轻量级的 Python Web 框架,它被广泛用于构建 Web 应用程序和 API。Flask 简单易用,具有灵活性和可扩展性,是许多开发者喜欢用其构建项目的原因。本文将介绍 Flask 是什么以及如何使用它来构建 Web 应用程序,同时提供一…...

C. Mark and His Unfinished Essay - 思维

分析: 直接模拟操作会mle,可以每次复制记录对应源字符串的下标,可以记录每次字符串增加的长度的左右端点下标,可以发现左端点与读入的l是对应的,因此就可以向前移到l的位置,这样层层递归,就能找…...

Java的变量与常量

目录 变量 声明变量 变量的声明类型 变量的声明方式:变量名 变量名的标识符 初始化变量 常量 关键字final 类常量 总结 变量和常量都是用来存储值和数据的基本数据类型存储方式,但二者之间有一些关键差别。 变量 在Java中,每个变…...

C# Blazor 学习笔记(6):热重置问题解决

文章目录 前言热重置问题描述解决方法演示 总结 前言 我最近在使用Blazor的时候,使用了BootstrapBlazor(以下简称BB)创建模板的时候,发现热重置无效。经过了一上午的折腾,我终于解决了这个问题。 热重置 问题描述 …...

一百四十六、Xmanager——Xmanager5连接Xshell7并控制服务器桌面

一、目的 由于kettle安装在Linux上,Xshell启动后需要Xmanager。而Xmanager7版本受限、没有免费版,所以就用Xmanager5去连接Xshell7 二、Xmanager5安装包来源 (一)注册码 注册码:101210-450789-147200 &#xff08…...

用Rust实现23种设计模式之 模板方法模式

关注我,学习Rust不迷路!! 模板方法模式是一种行为型设计模式,它定义了一个算法的骨架,将一些步骤的实现延迟到子类中。以下是模板方法模式的优点和使用场景: 优点: 提高代码复用性&#xff1…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制&#xff0…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...

网络编程(UDP编程)

思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

《C++ 模板》

目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能

1. 开发环境准备 ​​安装DevEco Studio 3.1​​: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK ​​项目配置​​: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist

现象: android studio报错: [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决: 不要动CMakeLists.…...

嵌入式常见 CPU 架构

架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集,单周期执行;低功耗、CIP 独立外设;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel(原始…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !

我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...