单例模式和工厂模式

目录

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

一、单例模式

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);}
}

二、工厂模式

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

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

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

举个例子:

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

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

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

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

 

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

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

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

 

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

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

相关文章

AWS——01篇(AWS入门 以及 AWS之EC2实例及简单实用)

AWS——01篇(AWS入门 以及 AWS之EC2实例及简单实用) 1. 前言2. 创建AWS账户3. EC23.1 启动 EC2 新实例3.1.1 入口3.1.2 设置名称 选择服务3.1.3 创建密钥对3.1.4 网络设置——安全组3.1.4.1 初始设置3.1.4.2 添加安全组规则(开放新端口&…

【夜深人静学习数据结构与算法 | 第十二篇】动态规划——背包问题

目录 前言: 01背包问题: 二维数组思路: 一维数组思路: 总结: 前言: 在前面我们学习动态规划理论知识的时候,我就讲过要介绍一下背包问题,那么今天我们就来讲解一下背包问题。 在这…

Tailwind CSS:简洁高效的工具,提升前端开发体验

112. Tailwind CSS:简洁高效的工具,提升前端开发体验 1. 什么是Tailwind CSS? Tailwind CSS是由Adam Wathan、Jonathan Reinink、David Hemphill和Steve Schoger等人共同创建的一种现代CSS框架。与传统的CSS框架不同,Tailwind CS…

mysql安装教程保姆级

MySQL免安装本地运行 1.下载MySQL2.创建install.bat3.init.sql 初始创建4.环境变量配置5.运行 install.bat 管理员权限运行6.连接成功遇到的问题 1.下载MySQL ①地址:https://downloads.mysql.com/archives/community/ ②解压 2.创建install.bat 放在mysql>b…

AI相机“妙鸭相机”原理分析和手动实现方案

妙鸭相机 一个通过上传大约20张照片,生成专属自拍。在2023年7月末爆火,根据36Kr报道,妙鸭相机系阿里系产品,挂靠在阿里大文娱体系下,并非独立公司。 使用方法是上传20张自拍照片,之后可以选择模板生成自己…

xlrd与xlwt操作Excel文件详解

Python操作Excel的模块有很多,并且各有优劣,不同模块支持的操作和文件类型也有不同。下面是各个模块的支持情况: .xls.xlsx获取文件内容写入数据修改文件内容保存样式调整插入图片xlrd√√√xlwt√√√√√xlutils√√√√xlwings√√√√√…

[openCV]基于拟合中线的智能车巡线方案V1

import cv2 as cv import os import numpy as np# 遍历文件夹函数 def getFileList(dir, Filelist, extNone):"""获取文件夹及其子文件夹中文件列表输入 dir:文件夹根目录输入 ext: 扩展名返回: 文件路径列表"""newDir d…

【具有非线性反馈的LTI系统识别】针对反馈非线性的LTI系统,提供非线性辨识方案(SimulinkMatlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

Stable Diffusion教程(7) - PS安装AI绘画插件教程

配套教程视频:https://v.douyin.com/Uyux9F6/ 1. 前置条件 安装了stable diffusion 还没安装的从知识库安装 阿超的AI绘画知识库 语雀 安装了ps2023 还没安装的从网盘下载Win版 PS 2023【必须win10、11】.rar官方版下载丨最新版下载丨绿色版下载丨APP下载-12…

字典与数组第5讲:数组区域内,数组公式的编辑和删除

【分享成果,随喜正能量】我们的心和宇宙本是相通的,所以生命内在蕴含了无限的智慧,但在没有开发没有证悟之前,生命是渺小而短暂的……..。 《VBA数组与字典方案》教程(10144533)是我推出的第三套教程&#…

Idea中侧面栏不见了,如何设置?

一、打开idea点击File然后点击Setting 二、点击Appearance,然后划到最下面,勾选Show tool windows bars和Side-by-side layout on the left 三、侧面栏目正常显示

pyspark使用XGboost训练模型实例

遇到一个还不错的使用Xgboost训练模型的githubhttps://github.com/MachineLP/Spark-/tree/master/pyspark-xgboost 1、这是一个跑通的代码实例,使用的是泰坦尼克生还数据,分类模型。 这里使用了Pipeline来封装特征处理和模型训练步骤,保存为…

【LeetCode每日一题】——304.二维区域和检索-矩阵不可变

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 矩阵 二【题目难度】 中等 三【题目编号】 304.二维区域和检索-矩阵不可变 四【题目描述】 …

JS进阶-Day3

🥔:永远做自己的聚光灯 JS进阶-Day1——点击此处(作用域、函数、解构赋值等) JS进阶-Day2——点击此处(深入对象之构造函数、实例成员、静态成员等;内置构造函数之引用类型、包装类型等) 更多JS…

【C++】—— 多态常见的笔试和面试问题

序言: 在上期,我们对多态进行了详细的讲解。本期,我给大家带来的是关于有关多态常见的笔试和面试问题,帮助大家理解记忆相关知识点。 目录 (一)概念查考 (二)问答题 1、简述一下…

C数据结构与算法——哈希表/散列表创建过程中的冲突与聚集(哈希查找) 应用

实验任务 (1) 掌握散列算法(散列函数、散列存储、散列查找)的实现; (2) 掌握常用的冲突解决方法。 实验内容 (1) 选散列函数 H(key) key % p,取散列表长 m 为 10000,p 取小于 m 的最大素数; (2) 测试 α…

阿里云安全组设置

简介​ 云主机安全组必须打开如下端口: ssh:22http:80https:443ftp:21、20000~30000 阿里云安全组端口开放教程​ 腾讯云安全组端口开放教程​ 华为云安全组端口开放教程​

C 语言高级2-多维数组,结构体,递归操作

1. 多维数组 1.1 一维数组 元素类型角度:数组是相同类型的变量的有序集合内存角度:连续的一大片内存空间 在讨论多维数组之前,我们还需要学习很多关于一维数组的知识。首先让我们学习一个概念。 1.1.1 数组名 考虑下面这些声明&#xff1…

华为云低代码平台Astro Canvas 搭建汽车展示大屏——实验指导手册

实验背景 大屏应用Astro Canvas是华为云低代码平台Astro的子服务之一,是以数据可视化为核心,以屏幕轻松编排,多屏适配可视为基础,用户可通过图形化界面轻松搭建专业水准的数据可视化大屏。例如汽车展示大屏、监控大屏、项目开发大…

docker端口映射详解(随机端口、指定IP端口、随意ip指定端口、指定ip随机端口)

目录 docker端口映射详解 一、端口映射概述: 二、案例实验: 1、-P选项,随机端口 2、使用-p可以指定要映射到的本地端口。 Local_Port:Container_Port,任意地址的指定端口 Local_IP:Local_Port:Container_Port 映射到指定地…