设计模式———单例模式

单例也就是只能有一个实例,即只创建一个实例对象,不能有多个。

可能会疑惑,那我写代码的时候注意点,只new一次不就得了。理论上是可以的,但在实际中很难实现,因为你无法预料到后面是否会脑抽一下~~因此我们就可以通过编码技巧,让编译器来帮助我们检查约束。

单例模式又分为两种:饿汉式和懒汉式区别在于实例对象创建的时机不同

0x00 饿汉式

说到饿汉,那一定是很饥饿的人,很急切,也就是说这个实例对象创建的时机比较早,在类加载的时候就创建了。

要点

  • 1、由于创建时机较早,设置为静态即可在类加载的时候就可以创建实例对象

  • 2、由于只能创建一个实例对象,因此将构造函数私有化

  • 3、对外提供一个获取实例对象的静态类方法

    public class Singleton{private static Singleton instance = new Singleton();public static Singleton getInstance(){return instance;}private Singleton(){}
    }

0x01 懒汉式

懒汉,顾名思义,很懒,比较缓慢,也就是说这个实例对象是在使用到的时候才去创建。

要点

  • 1、一开始设置为null,直到在使用的时候再去new实例对象

  • 2、由于只能创建一个实例对象,因此将构造函数私有化

3、对外提供一个获取实例对象的静态类方法

public class SingletonLazy {private static SingletonLazy instance = null;public static SingletonLazy getInstance(){if(instance == null){instance = new SingletonLazy();}return instance;}private SingletonLazy(){}
}

0x02 单例模式与多线程

众所周知,许多在单线程环境下的代码一到多线程环境下就会出错,在此将对单例模式在多线程下的编写进行调整优化~

先来看看饿汉式的代码,我们发现这个实现的方式是在类加载的时候就去实例化对象了,而后续在多线程中掉用getInstance方法来获取实例对象的时候,只会进行返回操作,即”只读”因此多个线程在操作的时候是安全的

再来看看懒汉式,可以发现在调用getInstance方法的时候,会进行修改操作,如果恰巧有两个线程同时进入了if,然后就会进行new操作,那不就创建了多个实例对象了,如果恰巧每个对象不是很轻量,可能有很多属性,加载了几十G的数据到内存中......

因此解决方法是进行加锁操作,保证if和new操作是原子的

public class SingletonLazy {private static SingletonLazy instance = null;public static SingletonLazy getInstance(){synchronized(SingletonLazy.class){if(instance == null){instance = new SingletonLazy();}}return instance;}private SingletonLazy(){}
}

这样修改以后,就能保证if和new操作是一个整体,此时线程的安全问题就得到了改善。

但是这样写是否就真的完了呢?

答案:不是。因为我们是单例模式,所以只需要new一次之后,实例对象就不可能是null了,后续直接return对象就行了。假如我们现在已经创建好了一个对象,当后续有多个线程去调用getInstance方法的时候,需要去获取到这把锁才能进行返回操作,也就是我们的多线程变成了串行化,并发程度几乎没了~

那是否有办法,既可以保证代码线程安全,又不会对执行效率产生大的影响呢?

如下代码:

public class SingletonLazy {private static SingletonLazy instance = null;public static SingletonLazy getInstance(){if(instance == null){synchronized(SingletonLazy.class){if(instance == null){instance = new SingletonLazy();}}}return instance;}private SingletonLazy(){}
}

与前面的代码对比,可以发现,在外层多加了一个if语句。

分析

当有多个线程进入了第一个if语句,此时里面有一个加锁操作,这样可以保证只创建了一个实例对象。假设此时已经创建好了一个实例对象了,当下一次又有多个线程进来时,由于instance不为null,直接返回了,并不会进入到加锁操作,此时并发并没有再受到影响了,既保证了正确又保证了效率。

总结

在锁外面的if语句保证了是否要进行加锁操作,即如果还没有创建实例对象,此时线程由于需要进行修改操作要进行加锁才能保证安全,如果已经创建了实例对象,此时线程安全了就不用加锁了。

在锁里面的if语句是来保证只创建一个实例对象。

0x03 指令重排序

上述代码其实还不够安全,还存在指令重排序的问题。

指令重排序,本质是编译器为了提高执行效率,调整原有的代码执行顺序(前提是保持逻辑不变)。

接下来具体问题具体分析:

上述的代码中,出现了一个new操作,而new操作其实是分三步的,第一步,申请内存空间,第二步,在内存空间上构造对象,第三步,把内存地址赋值给对象引用。一般是按照1,2,3的顺序执行的,但是编译器优化过后,可能会按照1,3,2的顺序执行,在单线程环境下都可以,但是在多线程环境下则不是。如果线程1是按照1,3,2顺序执行完了3后,线程2此时返回了这个实例对象,但是此时这个对象还没有初始化呢,也就是说这是个非法对象,当我们的线程2去访问这个对象的属性或方法时,此时就是非法,会出错。此时我们还需要再对instance加一个限制操作,即用volatile来修饰instance。

最终代码

public class SingletonLazy {private static volatile SingletonLazy instance = null;public static SingletonLazy getInstance(){if(instance == null){synchronized(SingletonLazy.class){if(instance == null){instance = new SingletonLazy();}}}return instance;}private SingletonLazy(){}
}

至此,这段“健壮”的代码基本可以满足使用了~

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

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

相关文章

【Pytorch】Conv1d

conv1d 先看看官方文档 再来个简单的例子 import torch import numpy as np import torch.nn as nndata np.arange(1, 13).reshape([1, 4, 3]) data torch.tensor(data, dtypetorch.float) print("[data]:\n", data) conv nn.Conv1d(in_channels4, out_channels1…

启明智显应用分享|基于ESP32-S3方案的SC01PLUS彩屏与chatgpt融合应用DEMO

今天将带大家真实体验科技与智慧的完美融合——SC01PLUS与ChatGPT的深度融合DEMO效果呈现。 彩屏的清晰显示与ChatGPT的精准回答,将为我们带来前所未有的便捷与高效。 SC01PLUS是启明智显基于ESP32-S3打造的一款3.5寸480*320分辨率的彩屏产品,您可以看…

【Git】git命令大全(持续更新)

本文架构 0.描述git简介术语 1.常用命令2. 信息管理新建git库命令更改存在库设置获取当前库信息 3.工作空间相关将工作空间文件添加到缓存区(增)从工作空间中移除文件(删)撤销提交 4.远程仓库相关同步远程仓库分支 (持…

高版本Android studio 使用Markdown无法预览(已解决)

目录 概述 解决方法 概述 本人升级Android studio 当前版本为Android Studio Jellyfish | 2023.3.1 RC 2导致Markdown无法预览。 我尝试了很多网上的方法都无法Markdown解决预览问题,包括升级插件、安装各种和Markdown相关的插件及使用“Choose Boot Java Runtim…

一文了解OCI标准、runC、docker、contianerd、CRI的关系

docker和contanerd都是流行的容器运行时(container runtime);想讲清楚他们两之间的关系,让我们先从runC和OCI规范说起。 一、OCI标准和runC 1、OCI(open container initiative) OCI是容器标准化组织为了…

利用动态规划优化10年投资回报:策略、证明与算法分析

利用动态规划优化10年投资回报:策略、证明与算法分析 a. 存在最优投资策略的证明b. 最优子结构性质的证明c. 最优投资策略规划算法设计d. 新限制条款下最优子结构性质的证明 在面对投资策略规划问题时,我们的目标是在10年后获得最大的回报。Amalgamated投…

牛客 NC205 跳跃游戏(三)【中等 贪心 Java,Go,PHP】

题目 题目链接: https://www.nowcoder.com/practice/14abdfaf0ec4419cbc722decc709938b 思路 参考答案Java import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可*** …

Go 单元测试之Mysql数据库集成测试

文章目录 一、 sqlmock介绍二、安装三、基本用法四、一个小案例五、Gorm 初始化注意点 一、 sqlmock介绍 sqlmock 是一个用于测试数据库交互的 Go 模拟库。它可以模拟 SQL 查询、插入、更新等操作,并且可以验证 SQL 语句的执行情况,非常适合用于单元测试…

基于SpringBoot+Vue社区医院服务平台(源码+文档+包运行)

一.系统概述 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了社区医院信息平台的开发全过程。通过分析社区医院信息平台管理的不足,创建了一个计算机管理社区医院信息平台的方案。文章介绍了社区医院信息…

如何在Linux CentOS部署宝塔面板并实现固定公网地址访问内网宝塔

文章目录 一、使用官网一键安装命令安装宝塔二、简单配置宝塔,内网穿透三、使用固定公网地址访问宝塔 宝塔面板作为建站运维工具,适合新手,简单好用。当我们在家里/公司搭建了宝塔,没有公网IP,但是想要在外也可以访问内…

QAnything部署Mac m1环境

本次安装时Qanything已经更新到了v1.3.3,支持纯python安装。安装过程比较简单,如下: QAnything/README_zh.md at qanything-python-v1.3.1 netease-youdao/QAnything GitHub 首先需要用Anaconda3创建隔离环境,简要说明下Anaco…

春藤实业启动SAP S/4HANA Cloud Public Edition项目,与工博科技携手数字化转型之路

3月11日,广东省春藤实业有限公司(以下简称“春藤实业”)SAP S/4HANA Cloud Public Edition(以下简称“SAP ERP公有云”)项目正式启动。春藤实业董事长陈董、联络协调项目经理慕总、内部推行项目经理陈总以及工博董事长…

酒店水电能源计量管理系统

酒店水电能源计量管理系统是一种针对酒店行业设计的能源管理系统,旨在实现对水电能源的计量、监测和管理。本文将从系统特点、构成以及带来的效益三个方面展开介绍。 系统特点 1.多元化计量:该系统能够对酒店内的水、电能源进行多元化计量,…

软件项目总体测试计划(Word原件2024)

一、 前言 (一) 背景 (二) 目的 (三) 测试目标 (四) 适用范围与读者对象 (五) 术语与缩写 二、 软件测试实施流程 (一) 测试工作总体流…

2024年MathorCup数学建模C题物流网络分拣中心货量预测及人员排班解题文档与程序

2024年第十四届MathorCup高校数学建模挑战赛 C题 物流网络分拣中心货量预测及人员排班 原题再现: 电商物流网络在订单履约中由多个环节组成,图1是一个简化的物流网络示意图。其中,分拣中心作为网络的中间环节,需要将包按照不同流…

【Python基础】MySQL

文章目录 [toc]创建数据库创建数据表数据插入数据查询数据更新 个人主页:丷从心 系列专栏:Python基础 学习指南:Python学习指南 创建数据库 import pymysqldef create_database():db pymysql.connect(hostlocalhost, userroot, passwordr…

Maven多模块管理

Maven多模块管理 在了解怎么进行Maven多模块管理之前,先聊聊为什么要进行Maven多模块管理 为什么要Maven多模块管理? 在传统的单体架构开发下,一个项目中的依赖只需要使用一个pom.xml文件管理即可。但是随着微服务的流行,将原有…

【IT运维入门(ITHW)系列】之「快速部署」第二期清单(持续更新)

ITHW是Information Technology Hello World的缩写简拼。意在提供IT领域的入门相关知识,近期给大家带来的是主流技术选型的快速部署系列,意在最大程度地简化部署过程,以便能快速体验或测试相关技术选型。 「快速部署」第一期清单 ITHW快捷部署…

Python零基础从小白打怪升级中~~~~~~~多线程

线程安全和锁 一、全局解释器锁 首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。 GIL全称global interpreter lock,全局解释器锁。 每个线程在执行的时候都需要先获取GIL,保证同一时刻只…

MR-JE-70A 三菱MR-JE伺服驱动器(750W通用型)

三菱MR-JE伺服驱动器(750W通用型) MR-JE-70A外部连接,MR-JE-70A用户手册,MR-JE-70A 三相或单相AC220V三菱通用型伺服放大器750W,配套电机HG-SN52J-S100、HG-KN73J-S100。 MR-JE-70A参数说明:伺服驱动器通用型750W,三相或单相AC200V~240V 三…