设计模式 - 创建型模式_7种单例模式实现

news2024/11/27 19:36:57

文章目录

  • 创建型模式
  • 概述
  • Case
  • 7种单例模式实现
    • 静态类使⽤
    • 懒汉模式(线程不安全)
    • 懒汉模式(线程安全)
    • 饿汉模式(线程安全)
    • 使⽤类的内部类(线程安全)
    • 双重锁校验(线程安全)
    • CAS「AtomicReference」(线程安全)
    • Effective Java作者推荐的枚举单例(线程安全)
  • 小结

在这里插入图片描述


创建型模式

创建型模式提供创建对象的机制, 能够提升已有代码的灵活性和可复⽤性。

类型实现要点
工厂方法定义⼀个创建对象的接⼝,让其⼦类⾃⼰决定实例化哪⼀个⼯⼚类,⼯⼚模式使其创建过程延迟到⼦类进⾏。
抽象工厂提供⼀个创建⼀系列相关或相互依赖对象的接⼝,⽽⽆需指定它们具体的类。
建造者将⼀个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示
原型⽤原型实例指定创建对象的种类,并且通过拷⻉这些原型创建新的对象。
单例保证⼀个类仅有⼀个实例,并提供⼀个访问它的全局访问点。

概述

在设计模式中按照不同的处理⽅式共包含三⼤类:

  • 创建型模式
  • 结构型模式
  • ⾏为模式

其中创建型模式⽬前已经搞了其中的四个:

  • ⼯⼚⽅法模式
  • 抽象⼯⼚模式
  • 建造者模式
  • 原型模式
  • 除此之外还有最后⼀个 单例模式

在这里插入图片描述
单例模式可以说是整个设计中最简单的模式之⼀,在编程开发中经常会遇到这样⼀种场景,那就是需要保证⼀个类只有⼀个实例哪怕多线程同时访问,并需要提供⼀个全局访问此实例的点。

单例模式主要解决的是,⼀个全局使⽤的类频繁的创建和消费,从⽽提升提升整体的代码的性能。


Case

⽇常开发所能⻅到的,例如:

  1. 数据库的连接池不会反复创建
  2. spring中⼀个单例模式bean的⽣成和使⽤
  3. 在我们平常的代码中需要设置全局的的⼀些属性保存

在我们的⽇常开发中⼤致上会出现如上这些场景中使⽤到单例模式,虽然单例模式并不复杂但是使⽤⾯却⽐较⼴。


7种单例模式实现

设计模式 - 创建型模式_ 单例模式 Singleton Pattern

并发编程-09安全发布对象+单例模式详解

单例模式的实现⽅式⽐较多,主要在实现上是否⽀持懒汉模式、是否线程安全中运⽤各项技巧。

当然也有⼀些场景不需要考虑懒加载也就是懒汉模式的情况,会直接使⽤ static 静态类或属性和⽅法的⽅式进⾏处理,供外部调⽤。

那么接下来我们就通过实现不同⽅式的实现单例模式。


静态类使⽤

public class Singleton_00 {

    public static Map<String,String> cache = new ConcurrentHashMap<String, String>();

}

  • 以上这种⽅式在平常的业务开发中⾮常场常⻅,这样静态类的⽅式可以在第⼀次运⾏的时候直接初始化Map类,同时这⾥我们也不需要到延迟加载在使⽤。
  • 在不需要维持任何状态下,仅仅⽤于全局访问,这个使⽤使⽤静态类的⽅式更加⽅便。
  • 但如果需要被继承以及需要维持⼀些特定状态的情况下,就适合使⽤单例模式。

懒汉模式(线程不安全)

public class Singleton_01 {

    private static Singleton_01 instance;

    private Singleton_01() {
    }

    public static Singleton_01 getInstance(){
        if (null != instance) return instance;
        return new Singleton_01();
    }

}

  • 单例模式有⼀个特点就是不允许外部直接创建,也就是 new Singleton_01() ,因此这⾥在默认的构造函数上添加了私有属性 private 。
  • ⽬前此种⽅式的单例确实满⾜了懒加载,但是如果有多个访问者同时去获取对象实例,就会造成多个同样的实例并存,从⽽没有达到单例的要求。

懒汉模式(线程安全)

public class Singleton_02 {

    private static Singleton_02 instance;

    private Singleton_02() {
    }

    public static synchronized Singleton_02 getInstance(){
        if (null != instance) return instance;
        return new Singleton_02();
    }

}

此种模式虽然是安全的,但由于把锁加到⽅法上后,所有的访问都因需要锁占⽤导致资源的浪费。 如果不是特殊情况下,不建议此种⽅式实现单例模式。


饿汉模式(线程安全)

public class Singleton_03 {

    private static Singleton_03 instance = new Singleton_03();

    private Singleton_03() {
    }

    public static Singleton_03 getInstance() {
        return instance;
    }

}

  • 此种⽅式与我们开头的第⼀个实例化 Map 基本⼀致,在程序启动的时候直接运⾏加载,后续有外部需要使⽤的时候获取即可。

  • 但此种⽅式并不是懒加载,也就是说⽆论程序中是否⽤到这样的类都会在程序启动之初进⾏创建。


使⽤类的内部类(线程安全)

public class Singleton_04 {

    private static class SingletonHolder {
        private static Singleton_04 instance = new Singleton_04();
    }

    private Singleton_04() {
    }

    public static Singleton_04 getInstance() {
        return SingletonHolder.instance;
    }

}

  • 使⽤类的静态内部类实现的单例模式,既保证了线程安全有保证了懒加载,同时不会因为加锁的⽅式耗费性能。
  • 这主要是因为JVM虚拟机可以保证多线程并发访问的正确性,也就是⼀个类的构造⽅法在多线程环境下可以被正确的加载。
  • 此种⽅式也是⾮常推荐使⽤的⼀种单例模式

双重锁校验(线程安全)

public class Singleton_05 {

    private static Singleton_05 instance;

    private Singleton_05() {
    }

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

}

  • 双重锁的⽅式是⽅法级锁的优化,减少了部分获取实例的耗时。
  • 同时这种⽅式也满⾜了懒加载。

CAS「AtomicReference」(线程安全)

import java.util.concurrent.atomic.AtomicReference;

public class Singleton_06 {

    private static final AtomicReference<Singleton_06> INSTANCE = new AtomicReference<Singleton_06>();

    private static Singleton_06 instance;

    private Singleton_06() {
    }

    public static final Singleton_06 getInstance() {
        for (; ; ) {
            Singleton_06 instance = INSTANCE.get();
            if (null != instance) return instance;
            INSTANCE.compareAndSet(null, new Singleton_06());
            return INSTANCE.get();
        }
    }

    public static void main(String[] args) {
        System.out.println(Singleton_06.getInstance());  
        System.out.println(Singleton_06.getInstance()); 
    }


}
  • java并发库提供了很多原⼦类来⽀持并发访问的数据安全性 AtomicInteger 、 AtomicBoolean 、 AtomicLong 、 AtomicReference
  • AtomicReference 可以封装引⽤⼀个V实例,⽀持并发访问如上的单例⽅式就是使⽤了这样的⼀个特点。
  • 使⽤CAS的好处就是不需要使⽤传统的加锁⽅式保证线程安全,⽽是依赖于CAS的忙等算法,依赖于底层硬件的实现,来保证线程安全。相对于其他锁的实现没有线程的切换和阻塞也就没有了额外的开销,并且可以⽀持较⼤的并发性。
  • 当然CAS也有⼀个缺点就是忙等,如果⼀直没有获取到将会处于死循环中。

Effective Java作者推荐的枚举单例(线程安全)


public enum Singleton_07 {

    INSTANCE;
    public void test(){
        System.out.println("hi~");
    }

}

约书亚·布洛克(英语:Joshua J. Bloch,1961年8⽉28⽇-),美国著名程序员。他为Java平台设计并实作了许多的功能,曾担任Google的⾸席Java架构师(Chief Java Architect)。

  • Effective Java 作者推荐使⽤枚举的⽅式解决单例模式,此种⽅式可能是平时最少⽤到的。
  • 这种⽅式解决了最主要的: 线程安全、⾃由串⾏化、单⼀实例。

调⽤⽅式:

 @Test
    public void test() {
        Singleton_07.INSTANCE.test();
    }
  • 这种写法在功能上与共有域⽅法相近,但是它更简洁,⽆偿地提供了串⾏化机制,绝对防⽌对此实例化,即使是在⾯对复杂的串⾏化或者反射攻击的时候。虽然这中⽅法还没有⼴泛采⽤,但是单元素的枚举类型已经成为实现Singleton的最佳⽅法。
  • 但此种⽅式在存在继承场景下是不可⽤的。

小结

这里我们看到了懒汉、饿汉、线程是否安全、静态类、内部类、加锁、串⾏化等等各种技能。

在平时的开发中如果可以确保此类是全局可⽤不需要做懒加载,那么直接创建并给外部调⽤即可。

但如果是很多的类,有些需要在⽤户触发⼀定的条件后才显示,那么⼀定要⽤懒加载。线程的安全上可以按需选择。

在这里插入图片描述

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

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

相关文章

MySQL约束详解

目录 概念 作用 分类 MySQL约束——主键约束 概念 操作 操作——添加单列主键 操作——添加多列主键&#xff08;联合主键&#xff09; 操作——删除主键约束 MySQL约束-自增长约束(auto_increment) 概念 语法 操作 特点 指定自增字段初始值 delete和truncate在删…

零基础学JavaWeb开发(二十六)之 nginx(2)

5、基于Nginx解决跨域问题 5.1、什么是网站跨域问题 前端部署 html.mayikt.com /index.html 后端部署 api.mayikt.com/ 接口 java 浏览器访问&#xff1a;http://html.mayikt.com/user.html 页面里面 ajax 请求&#xff1a;http://api.mayikt.com/getUser 浏览器访问&#…

力扣刷题记录——697. 数组的度、728. 自除数 、821. 字符的最短距离

本专栏主要记录力扣的刷题记录&#xff0c;备战蓝桥杯&#xff0c;供复盘和优化算法使用&#xff0c;也希望给大家带来帮助&#xff0c;博主是算法小白&#xff0c;希望各位大佬不要见笑&#xff0c;今天要分享的是——《力扣刷题记录——697. 数组的度、728. 自除数 、821. 字…

腾讯云开发者2022年度热文盘点

01十亿人都在用的健康码运维体系如何设计&#xff1f;随着疫情防控模式的迭代&#xff0c;健康码访问DAU逐渐趋于下跌&#xff0c;意味着健康码将逐步完成历史使命&#xff0c;见证着疫情的结束。本文特邀腾讯研发工程师李雄政将从技术架构、可观测体系、运营保障体系等运维体系…

星德胜冲刺上交所上市:计划募资约10亿元,朱云舫为实际控制人

近日&#xff0c;星德胜科技&#xff08;苏州&#xff09;股份有限公司&#xff08;下称“星德胜”&#xff09;预披露更新招股书&#xff0c;准备在上海证券交易所主板上市。据贝多财经了解&#xff0c;星德胜于2022年7月1日递交上市申请&#xff0c;海通证券为其保荐机构。 …

java ssm学生成绩查询考务系统

1 绪论 1 1.1 项目背景 1 1.2 项目研究的目的和方法 1 1.2.1 项目的研究目的 1 1.2.2 开发方法及步骤 1 1.3 本章小结 2 2. 开发平台介绍 3 2.1 系统开发环境 3 2.1.1 Eclipse 3 2.2 相关技术 4 2.2.1 JSP技术 …

Web Spider 常见混淆EVAL、AA、JJ、JSFUCK

文章目录一、EVAL & constructor二、AA混淆三、JJ混淆四、JSFUCK五、针对混淆的方法简单hook总结混淆的原理&#xff1a;就是把原本清晰的代码故意搞得花里胡哨&#xff0c;让局外人看不懂&#xff1b; 混淆的目的&#xff1a;让逆向者很难理解代码的意图&#xff0c;无法读…

随笔集1.24

秋夜独坐经常性会出现啥都不想干的情况&#xff0c;似那黄鹤一去不复返&#xff0c;只留下脑海的白云千载空悠&#xff0c;对任何事情&#xff0c;读书、研究、游戏都提不起兴趣&#xff0c;每当此时静坐于窗下灯前&#xff0c;想起王摩诘所谓雨中山果落&#xff0c;灯下草虫鸣…

第二章-计算机网络物理层

文章目录计算机网络一、物理层1.1、物理层的基本概念1.2、数据通信的基本知识编码与调制编码调制混合调制信道的极限容量传输方式1.3、物理层下面的传输媒体导引型传输媒体非导引型传输媒体1.4、互联网接入技术电话网拨号接入数字用户线接入光纤同轴混合网接入光纤接入无线接入…

用友BIP对接外部旺店通企业版奇门系统

用友BIP对接外部旺店通企业奇门系统源系统:旺店通企业奇门慧策最先以旺店通ERP切入商家核心管理痛点——订单管理&#xff0c;之后围绕电商经营管理中的核心管理诉求&#xff0c;先后布局流量获取、会员管理、仓库管理等其他重要经营模块。慧策的产品线从旺店通ERP起步&#xf…

Jetpack架构组件库:Lifecycle、LiveData、ViewModel

Lifecycle 添加依赖 dependencies {def lifecycle_version "2.5.1" // ViewModelimplementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"// ViewModel utilities for Composeimplementation "androidx.lifecycle:lifecy…

解决nes_py在pip安装报错的问题

目录 项目场景&#xff1a; 问题描述 原因分析&#xff1a; 解决方案&#xff1a; 解决结果&#xff1a; 项目场景&#xff1a; 想跟随油管某视频复现强化学习方法玩超级马里奥的过程&#xff0c;结果在在Anaconda3虚拟环境中用pip安装nes_py时一直报错&#xff0c;报错信…

基于Python实现的图像文字识别OCR工具,包含GUI界面附完整版代码可直接运行

引言 最近在技术交流群里聊到一个关于图像文字识别的需求,在工作、生活中常常会用到,比如票据、漫画、扫描件、照片的文本提取。 博主基于 PyQt + labelme + PaddleOCR 写了一个桌面端的 OCR 工具,用于快速实现图片中文本区域自动检测 + 文本自动识别。 识别效果如下图所示:…

总结JDK中的时间日期类

在学习SpringMVC时&#xff0c;遇到了接收时间日期类型的参数的案例。 回顾JDK中与时间日期相关的API。 来系统地学习一下日期时间相关的API。 前置知识 在世界上有统一的时间标准 格林尼治时间&#xff0c;简称GMT&#xff08;以伦敦的本初子午线为标准&#xff09;&#x…

【BTC】数据结构

BTC 中对交易数据的存储主要涉及到了两种数据结构&#xff0c;一种是区块链&#xff0c;一种是 Merkle Tree。这两种数据结构组成了 BTC 中完整的区块链结构&#xff08;如下图所示&#xff09;&#xff0c;共同完成对数据的存储和验证&#xff0c;确保交易的有效性。 一、区块…

常见的反爬手段和解决思路

常见的反爬手段和解决思路 学习目标 了解 服务器反爬的原因了解 服务器常反什么样的爬虫了解 反爬虫领域常见的一些概念了解 反爬的三个方向了解 常见基于身份识别进行反爬了解 常见基于爬虫行为进行反爬了解 常见基于数据加密进行反爬 1 服务器反爬的原因 爬虫占总PV(PV是指…

基于nodejs+vue的中国古诗词的设计与实现

目 录 摘要 I Abstract II 1 绪论 1 1.1 选题背景 1 1.2 选题意义 1 1.3 研究内容 2 2 相关技术介绍 3 3 系统分析 5 3.1可行性分析 5 3.1.1 操作可行性 5 3.1.2 经济可行性 5 3.1.3 技术可行性 5 3.2 需求分析 5 3.2.1非功能性需求 …

Python学习——(数据类型及其常用函数)

目录 一、数据类型 判断数据类型type() 二、数据类型的转换 三、运算符 (一)算数运算符 (二)赋值运算符 (三)复合赋值运算符 (四)比较运算符 (五)逻辑运算符 四、输入输出 (一)输出 (二)输入 五、各数据类型常用函数 (一)数值函数 1.绝对值abs(x) 2.最大值max(…

Git + Jenkins 自动化 NGINX 发布简易实现

概述 之前基于 GitLab Jenkins 实现了简单的 NGINX 的自动化发布。 具体包含如下的组件&#xff1a; GitLab包括 GItLab 的 WebHook&#xff1b; Jenkins 及其插件&#xff1a;Generic Webhook TriggerPublish Over SSH &#x1f9e0;疑问&#xff1a; 为什么不用 Ansible&…

算法训练营DAY44|518. 零钱兑换 II、377. 组合总和 Ⅳ

这两道题是对于完全背包题型的另一个维度&#xff0c;都是求解给定背包容量求装满背包最多有几种方法的题目。两道题十分相像&#xff0c;但在遍历顺序上却又有着极其微妙的差别。 518. 零钱兑换 II - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/coin…