单例模式

news2024/11/19 21:30:20

单例模式

1. 单例模式介绍

image-20230102114059016

单例模式可以说是整个设计中最简单的模式之一,而且这种方式即使在没有看设计模式相关资料也会常用在编码开发中。

因为在编程开发中经常会遇到这样一种场景,那就是需要保证一个类只有一个实例哪怕多线程同时访问,并需要提供一个全局访问此实例的点。

综上以及我们平常的开发中,可以总结一条经验,单例模式主要解决的是,一个全局使用的类频繁的创建和消费,从而提升提升整体的代码的性能。

2. 案例场景

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

在我们的日常开发中大致上会出现如上这些场景中使用到单例模式,虽然单例模式并不复杂但是使用面却比较广。

3. 7种单例模式实现

静态类使用

public class Singleton_00 {

    public static Map<String,String> cache = new ConcurrentHashMap<String, String>();
    
}
  • 以上这种方式在我们平常的业务开发中非常常见,这样静态类的方式可以在第一次运行的时候直接初始化Map类,同时这里我们也不需要到延迟加载在使用。
  • 在不需要维持任何状态下,仅仅用于全局访问,这个使用使用静态类的方式更加方便。
  • 但如果需要被继承以及需要维持一些特定状态的情况下,就适合使用单例模式。

1.懒汉模式(线程不安全)

public class Singleton_01 {

    private static Singleton_01 instance;

    private Singleton_01() {
    }

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

}
  • 单例模式有一个特点就是不允许外部直接创建,也就是new Singleton_01(),因此这里在默认的构造函数上添加了私有属性 private
  • 目前此种方式的单例确实满足了懒加载,但是如果有多个访问者(多个线程)同时去获取对象实例,就有可能会创建多个实例,导致线程间拿到的对象是不一样的,从而没有达到单例的要求。

2.懒汉模式(线程安全)

public class Singleton_02 {

    private static Singleton_02 instance;

    private Singleton_02() {
    }

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

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

3. 饿汉模式(线程安全)

public class Singleton_03 {

    private static Singleton_03 instance = new Singleton_03();

    private Singleton_03() {
    }

    public static Singleton_03 getInstance() {
        return instance;
    }

}
  • 此种方式与我们开头的第一个实例化Map基本一致,在程序启动的时候直接运行加载,后续有外部需要使用的时候获取即可。
  • 但此种方式并不是懒加载,也就是说无论你程序中是否用到这样的类都会在程序启动之初进行创建。
  • 那么这种方式导致的问题就像你下载个游戏软件,可能你游戏地图还没有打开呢,但是程序已经将这些地图全部实例化。到你手机上最明显体验就一开游戏内存满了,手机卡了,需要换了。

4.使用类的内部类(线程安全)

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虚拟机可以保证多线程并发访问的正确性,也就是一个类的构造方法在多线程环境下可以被正确的加载。
  • 此种方式也是非常推荐使用的一种单例模式

5.双重锁校验(线程安全)

public class Singleton_05 {

    private static volatile 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;
    }

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

6.CAS「AtomicReference」(线程安全)

public class Singleton_06 {

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

    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()); // org.itstack.demo.design.Singleton_06@2b193f2d
        System.out.println(Singleton_06.getInstance()); // org.itstack.demo.design.Singleton_06@2b193f2d
    }

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

7.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的最佳方法。

但也要知道此种方式在存在继承场景下是不可用的。

4.总结

  • 虽然只是一个很平常的单例模式,但在各种的实现上真的可以看到java的基本功的体现,这里包括了;懒汉、饿汉、线程是否安全、静态类、内部类、加锁、串行化等等。
  • 在平时的开发中如果可以确保此类是全局可用不需要做懒加载,那么直接创建并给外部调用即可。但如果是很多的类,有些需要在用户触发一定的条件后(游戏关卡)才显示,那么一定要用懒加载。线程的安全上可以按需选择。

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

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

相关文章

C#语言实例源码系列-实现IC卡的读写

专栏分享点击跳转>Unity3D特效百例点击跳转>案例项目实战源码点击跳转>游戏脚本-辅助自动化点击跳转>Android控件全解手册 &#x1f449;关于作者 众所周知&#xff0c;人生是一个漫长的流程&#xff0c;不断克服困难&#xff0c;不断反思前进的过程。在这个过程中…

机器学习:通俗理解马尔科夫随机场(MRF)及其应用(附例题)

目录0 写在前面1 无向概率图2 马尔科夫随机场3 马尔科夫独立性4 例题分析0 写在前面 机器学习强基计划聚焦深度和广度&#xff0c;加深对机器学习模型的理解与应用。“深”在详细推导算法模型背后的数学原理&#xff1b;“广”在分析多个机器学习模型&#xff1a;决策树、支持…

Git使用,在github中创建仓库

一.本地生成密钥&#xff1a; ssh-keygen //生成密钥 cat id_rsa.pub # 查看公钥 查看公钥&#xff0c;并将公钥添加到github的服务器上 二.创建文件&#xff0c;并且将文件上传到GitHub 设置全局用户信息&#xff1a; git config --global user.name dwerrwgit config…

LabVIEW NI数字万用表与开关握手扫描速率

LabVIEW NI数字万用表与开关握手扫描速率 在决定需要哪些设备来满足系统要求时&#xff0c;对扫描速率数据进行基准测试非常有用。数字万用表&#xff08;DMM&#xff09;和开关系统也是如此&#xff0c;因为扫描速率取决于数字万用表、开关和它们之间的触发器的速度。本文包含…

高并发系统设计 -- 抢红包设计

抢红包的业务分析 可以明显的看到打开了红包不一定可以抢到。这样做的好处是&#xff1a; 符合现实生活逻辑&#xff0c;有仪式感防止误领&#xff0c;发现不对劲可以马上退出流程拆的长一些&#xff0c;平摊高并发下的压力 预拆包&#xff1a;我在发红包的时候&#xff0c;就…

ansible的静态清单配置文件

清单文件 定义主机清单文件 清单中定义ansible将要管理的一批主机&#xff0c;这些主机也可以分配到组中&#xff0c;以进行集中管理。组中也可以包含子组&#xff0c;一台主机也可以是多个组中的成员。清单还可以设置应用到它所定义的主机和组的变量。 编写主机清单文件 主机…

归置归置,我的 2022

J3code杂文&#xff08;程序人生 # 年终总结&#xff09; 记得 2021 年我没有进行年终总结&#xff0c;也就没有发出过相关的内容出来。总结原因就是一个&#xff0c;躺平了&#xff0c;自毕业换工作之后&#xff0c;就一直在适应工作环境与生活环境中默默的度过了 2021 年。 …

你真的懂树吗?二叉树、AVL平衡二叉树、伸展树、B-树和B+树原理和实现代码详解...

树&#xff08;Tree&#xff09;是一种相当灵活的数据结构&#xff08;上一节已经详细讲解了基本的数据结构&#xff1a;线性表、栈和队列&#xff09;&#xff0c;你可能接触过二叉树&#xff0c;但是树的使用并不限于此&#xff0c;从简单的使用二叉树进行数据排序&#xff0…

(深度学习快速入门)第三章第一节:多层感知器简介

文章目录一&#xff1a;引入二&#xff1a;定义三&#xff1a;反向传播算法四&#xff1a;构建多层感知器完成波士顿房价预测一&#xff1a;引入 前文所讲到的波士顿房价预测案例中&#xff0c;涉及到的仅仅是一个非常简单的神经网络&#xff0c;它只含有输入层和输出层 但观…

vue3 antd项目实战——Form表单的重置与清空【resetFields重置表单未生效(手写重置函数)】

vue3 antd项目实战——resetFields重置表单无效【手写重置函数重置表单数据】关于form表单的文章合集场景复现原因分析解决方案(手写清空函数)关于form表单的文章合集 文章内容文章链接Form表单提交和校验https://blog.csdn.net/XSL_HR/article/details/128495087?spm1001.20…

面向对象定义一个hero类

问题定义一个hero类&#xff0c;属性有power&#xff0c;name&#xff0c;分别代表体力值和英雄的名字&#xff0c;体力值默认为100&#xff1b;方法有&#xff1a;1.行走的方法如果体力值为0&#xff0c;则输出不能行走&#xff0c;此英雌已死亡的信息&#xff1b;2.吃的方法&…

双非二本、已获HCIA认证的大二学生与C站相遇的2022

目录 前言 2022年1月、2月——迷茫 2022年3月~6月——调整规划 ​2022年7月——在CSDN发布第一篇博客 2022年8月——步入正轨&#xff0c;获得2022谷歌开发者大会入场名额 2022年9月~10月——开学季&#xff0c;收获季 2022年11月——第一次接触项目并去公司学习实践&…

01通信/协议一些简要概念

通信的目的 将一个设备的数据传送到另一个设备&#xff0c;扩展硬件系统。 通信协议 制定通信的规则&#xff0c;通信双方按照协议规则进行数据收发。 每一种通讯协议都有硬件与软件上的要求。 常见的协议 USART TX、RX 全双工 异步 单端 点对点 I2C SCL、SDA 半双…

百度大规模知识图谱构建及技术应用实践

省时查报告-专业、及时、全面的行研报告库省时查方案-专业、及时、全面的营销策划方案库【免费下载】2022年11月份热门报告盘点罗振宇2023年跨年演讲PPT原稿2023年&#xff0c;如何科学制定年度规划&#xff1f;《底层逻辑》高清配图‍基于深度学习的个性化推荐系统实时化改造与…

【Node.js实战】一文带你开发博客项目之登录(对接完毕,cookie、session、redis各司其职)

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;也会涉及到服务端 &#x1f4c3;个人状态&#xff1a; 在校大学生一枚&#xff0c;已拿多个前端 offer&#xff08;秋招&#xff09; &#x1f680;未…

【蓝桥杯选拔赛真题54】Scratch小猫钓鱼 少儿编程scratch图形化编程 蓝桥杯选拔赛真题讲解

目录 scratch小猫钓鱼 一、题目要求 编程实现 二、案例分析 1、角色分析

河道船只识别系统 yolov5

河道船只识别系统通过Python基于YOLOv5深度学习框架模型技术对画面中船只进行监测&#xff0c;如识别到有船只违规行为&#xff0c;立即抓拍告警同步回传给平台。YOLOv5是一种单阶段目标检测算法&#xff0c;该算法在YOLOv4的基础上添加了一些新的改进思路&#xff0c;使其速度…

写了个自动巡检多个接口地址的脚本!

作者&#xff1a;JackTian 来源&#xff1a;公众号「杰哥的IT之旅」 ID&#xff1a;Jake_Internet 转载请联系授权&#xff08;微信ID&#xff1a;Hc220088&#xff09; 原文链接&#xff1a;写了个自动巡检多个接口地址的脚本&#xff01; 没错&#xff0c;这次我结合工作运用…

【C语言】你对动态内存分配有多少了解呢

&#x1f3d6;️作者&#xff1a;malloc不出对象 ⛺专栏&#xff1a;《初识C语言》 &#x1f466;个人简介&#xff1a;一名双非本科院校大二在读的科班编程菜鸟&#xff0c;努力编程只为赶上各位大佬的步伐&#x1f648;&#x1f648; 目录前言一、什么是动态内存分配二、为什…

SpringBoot(二)【学习笔记】

SpringBoot的配置文件 之前SSM项目: 每一个框架都有自己的配置文件, 每一个配置文件头文件不一样, 需要找到每个框架的头文件 SpringBoot的配置文件: 所有的框架的配置项,都可以在application.properties文件配置, 如果自定义一些配置, 修改SpringBoot默认的配置项, 可以在appl…