【设计模式】单例模式详解

单例模式:

  • 定义:确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。为了防止出现重复的创建。
    单例模式是JAVA中最简单的设计模式之一。属于创建型设计模式,它提供了一种创建对象的最佳方式。
    这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有一个对象被创建。这个类提供了访问唯一对象的方法,可以直接访问,不需要new。
饿汉式:程序起始就初始化对象。
public class HungrySingle {
    // 私有空参构造方法,让初始化无法轻易被别人调用 (反射可以进行调用)
    private HungrySingle() {}

    // 在类中创建该类对象
    private static final HungrySingle single = new  HungrySingle();

    // 提供公共的访问方式,提供外接进行访问
    public static HungrySingle getInstance() {return single;}
}
  • 优点:
  1. 线程安全:由于实例在类加载时就创建,所以不存在多线程环境下的线程安全问题。
  2. 简单易用:实现简单,不需要考虑多线程同步等复杂性。
  • 缺点:
  1. 浪费资源:在程序启动时即创建实例,如果该实例在后续程序运行中很少被使用,就会造成资源浪费。
  2. 灵活性受限:无法实现延迟加载,不适合在实例创建过程中需要执行复杂操作的情况。
  3. 可能造成加载缓慢:如果实例初始化较为耗时,会导致程序启动较慢。
懒汉式:被外部类调用才创建实例
public class LazySingle {
    // 声明私有构造方法
    private LazySingle(){};

    // 单例对象实例
    private static LazySingle single;

    // 获取单例对象的静态方法
    public static synchronized LazySingle getInstance() {
        // 如果实例为空,则创建新实例
        if (single == null) {
            single = new LazySingle();
        }
        return single;
    }
}
  • 优点:
  1. 延迟加载:只有在需要时才创建实例,避免了资源的浪费。
  2. 灵活性高:可以根据需要进行实例化,适用于实例初始化较为复杂或耗时的情况。
  • 缺点:
  1. 性能受影响:由于需要在获取实例时进行同步操作,可能会影响性能。
  2. 复杂度增加:需要考虑线程安全性,可能需要使用同步锁等机制,增加了代码复杂度。
  3. 可能存在线程安全问题:在某些情况下,同步机制可能会引入死锁等问题,需要谨慎设计。
DCL双重检测锁
public class DoubleCheckLockSingle {
    // 使用volatile关键字确保instance在多线程环境下的可见性
    private volatile static DoubleCheckLockSingle instance;

    // 私有构造方法,避免外部直接实例化对象
    private DoubleCheckLockSingle() {
        // 私有构造方法
    }

    // 获取单例对象的静态方法
    public static DoubleCheckLockSingle getInstance() {
        // 第一次检查,避免不必要的同步
        if (instance == null) {
            // 同步块,确保在多线程环境下只有一个线程创建实例
            synchronized (DoubleCheckLockSingle.class) {
                // 第二次检查,防止多个线程同时进入同步块后重复创建实例
                if (instance == null) {
                    instance = new DoubleCheckLockSingle();
                }
            }
        }
        return instance;
    }
}
  • 优点:

优点:

  1. 延迟加载:只有在需要时才创建实例,避免了资源的浪费。
  2. 线程安全:通过双重检查锁机制,在多线程环境下保证了线程安全性。
  3. 性能较好:相比简单的懒汉式单例,双重检查锁在保证线程安全的同时减少了不必要的同步开销。
  • 缺点:
  1. 实现复杂:双重检查锁的实现较为复杂,需要考虑到多线程并发情况下的各种细节,容易出错。
  2. 可能存在指令重排问题:在某些情况下,由于指令重排的影响,可能会导致获取到未完全初始化的实例。
  3. 不适用于早期JDK版本:在早期的JDK版本中,由于JVM对volatile关键字的实现问题,可能会导致DCL失效。

在实际应用中,双重检查锁单例模式适用于需要延迟加载且对性能要求较高的情况,但需要谨慎考虑线程安全和实现复杂度。

静态内部类
public class StaticInnerClazzSingle {

    // 私有化构造方法,避免外部直接实例化
    private StaticInnerClazzSingle() {
        // 初始化操作
    }

    // 静态内部类,利用类加载机制实现延迟加载
    private static class SingletonHolder {
        private static final StaticInnerClazzSingle INSTANCE = new StaticInnerClazzSingle();
    }

    // 获取单例实例的方法
    public static StaticInnerClazzSingle getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
  • 优点
  1. 线程安全:利用类加载机制保证了线程安全,无需额外的同步措施。
  2. 延迟加载:只有在 getInstance() 方法被调用时才会加载内部类并初始化单例对象,实现了延迟加载。
  3. 简洁高效:代码简洁清晰,利用静态内部类的特性实现单例,高效且易于理解。
  • 缺点
  1. 不适用于非静态字段初始化:静态内部类只能访问外部类的静态成员,无法直接访问外部类的非静态成员。
  2. 可能过于晦涩:对于不熟悉静态内部类机制的开发人员,可能会造成理解上的困难。
  3. 无法传递参数:静态内部类实现的单例无法传递参数,因为在类加载时就初始化实例。

总体来说,静态内部类实现的单例模式是一种优雅且高效的实现方式,适合大多数情况下的单例需求,尤其适用于需要延迟加载且对线程安全有要求的场景。

枚举式单例模式
枚举类型是线程安全的,且只会加载一次。设计者充分的利用的这个特性来实现单例模式。且枚举模式不会被破坏,即不会因为反射而破坏这个实现
public enum EnumSingle {
    INSTANCE; // 单例实例

    // 枚举类的构造方法默认私有,保证外部无法实例化
    private EnumSingle() {
        // 初始化操作
    }

    // 获取单例实例的方法
    public static EnumSingle getInstance() {
        return INSTANCE;
    }
}
  • 优点:
  1. 线程安全:枚举类型在Java中天然线程安全,保证了单例的唯一性。
  2. 避免反射和序列化问题:枚举类型在序列化和反序列化过程中会自动处理实例化,避免反射攻击和序列化破坏单例的问题。
  3. 防止多次实例化:枚举类只能被实例化一次,避免了多次实例化的可能性。
  • 缺点:
  1. 无法懒加载:枚举类在类加载时就会被实例化,无法实现延迟加载。
  2. 无法传递参数:枚举类的构造方法无法传递参数,因为枚举实例在类加载时就被初始化。
  3. 不灵活:枚举类型本质上是类,无法继承其他类或实现其他接口,限制了扩展性。

总体来说,枚举方式实现单例模式是一种简洁高效且安全的方式,适合大多数单例需求,特别是在需要线程安全和避免反射攻击的情况下。然而,如果需要延迟加载或传递参数,枚举方式可能不适用。

存在的问题和解决方案

  1. 破坏单例模式
    除了枚举模式外,可以使用序列化和反射可以破坏单例模式。
    序列化原理: 反序列化读取对象是对象的copy。
    序列化和反序列化demo比较麻烦,笔者就不贴代码了,有兴趣的小伙伴可以自己尝试下。
/**
 * 反射破坏单例 以StaticInnerClazzSingle为例
 * 1. 获取StaticInnerClazzSingle类的Class对象。
 * 2. 获取该类的无参构造函数,并设置其可访问性。
 * 3. 通过构造函数创建一个新的HungrySingle实例。
 */
 	// 获取StaticInnerClazzSingle类的Class对象
	Class clazz = StaticInnerClazzSingle.class; 
	// 获取类的无参构造器
	Constructor cons = clazz.getDeclaredConstructor(); 
	// 设置构造器可访问
	cons.setAccessible(true); 
	// 利用构造器创建实例
	StaticInnerClazzSingle single = (StaticInnerClazzSingle) cons.newInstance(); 
  1. 解决方法
// 当进行反序列化时,会自动调用该方法,将方法的返回值返回
public Object readResolve() {
	return SingletonHolder.INSTANCE;
}
// 反射破坏解决办法
private static boolean flag = false// 私有构造方法
private StaticInnerClazzSingle() {
	if (flag) {
		throw new RuntimeException("不能创建多个异常");
	}
	// 将flag设置为true
	flag = true}
典型的单例模式

Runtime 饿汉式单例模式

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构与算法】快速排序(详解:快排的Hoare原版,挖坑法和双指针法|避免快排最坏时间复杂度的两种解决方案|小区间优化|非递归的快排)

引言 快速排序作为交换排序的一种,在排序界的影响力毋庸置疑,我们C语言中用的qsort,C中用的sort,底层的排序方式都是快速排序。相比于同为交换排序的冒泡,其效率和性能就要差的多了,本篇博客就是要重点介绍…

2024 ccfcsp认证打卡 2023 03 01 田地丈量

import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner in new Scanner(System.in);int n in.nextInt(); // 输入 n,表示矩形的数量int a in.nextInt(); // 输入 a,表示整个区域的长度int b in.nextInt()…

3.28学习总结

java 封装 封装体现了java的面向对象的特点,用户不用知道程序是如何运行的,只用按照所给的格式输入参数,便可得到对应的结果. 一个完整的封装需要每个实例变量都用private来修饰并拥有相应的public getter和setter方法. 代码 public class girl {private int age;public st…

C++:转义符(10)

在c中有一些字符无法被显示出来,所以需要使用些特殊字符加字母来展示 可以看到基本都是一个\加一个字母去只执行对应的一个效果 这里我选择几个对于当前来说比较重要的:\n ,\\ ,\t \n换行符 可以看到在c语言中他就是一个可以换行…

c++ 有名对象和匿名对象

c 有名对象和匿名对象 有名对象就是有名字的对象&#xff0c;匿名对象就是没有名字的对象。 #define _CRT_SECURE_NO_WARNINGS 1 using namespace std; #include<iostream> class score { public:score(){math 100;chinese 100;english 100;}score(int _math, int _…

GitHub开源项目权限管理-使用账号和个人令牌访问

1.打开后台账号设置 2.找到左下角的Developer settings 3.找到Personal access tokens 的 Tokens(classic) 4.选择创建新证书 5.填写证书信息 6.点击生成证书&#xff0c;复制证书并且保存起来&#xff08;血泪教训&#xff0c;证书只会在创建时显示一次&#xff0c;以后就再也…

<QT基础(5)>事件监听

事件监听 事件监听&#xff08;Event Handling&#xff09;是在程序中监视和响应发生的事件的一种机制。在Qt中&#xff0c;事件监听是一种常见的用于处理用户输入、系统事件以及其他类型事件的方法。通过事件监听&#xff0c;您可以在发生特定事件时捕获事件并执行相应的操作…

Ainx的多路由模式

&#x1f4d5;作者简介&#xff1a; 过去日记&#xff0c;致力于Java、GoLang,Rust等多种编程语言&#xff0c;热爱技术&#xff0c;喜欢游戏的博主。 &#x1f4d7;本文收录于Ainx系列&#xff0c;大家有兴趣的可以看一看 &#x1f4d8;相关专栏Rust初阶教程、go语言基础系列…

使用Zabbix监控NAS目录状态

在企业的数据存储和共享中,网络附加存储(NAS)扮演着至关重要的角色。为了确保NAS设备的稳定运行和数据的完整性,对其进行实时监控是必不可少的。Zabbix作为一款开源的网络监控解决方案,能够帮助我们实现这一目标。本文将介绍如何使用Zabbix监控NAS目录状态,以确保及时发现…

如何在CentOS使用Docker搭建MinIO容器并实现无公网ip远程访问本地服务

文章目录 前言1. Docker 部署MinIO2. 本地访问MinIO3. Linux安装Cpolar4. 配置MinIO公网地址5. 远程访问MinIO管理界面6. 固定MinIO公网地址 前言 MinIO是一个开源的对象存储服务器&#xff0c;可以在各种环境中运行&#xff0c;例如本地、Docker容器、Kubernetes集群等。它兼…

MySQL数据库 - 复杂查询(一)

一个不知名大学生&#xff0c;江湖人称菜狗 original author: Jacky Li Email : 3435673055qq.com Time of completion&#xff1a;2024.03.27 Last edited: 2024.03.27 目录 MySQL数据库 - 复杂查询&#xff08;一&#xff09; 第1关&#xff1a;交换工资 任务描述 相关知…

电平输入检测-定时器输入捕获

目录 一&#xff0c;引入 二&#xff0c;具体结构 三&#xff0c;实现步骤 四&#xff0c;PWM输入模式 一&#xff0c;引入 上篇博客&#xff0c;我们对于定时器的计数核心——时基单元作了细致的了解。这篇博文&#xff0c;我们来介绍定时器的四大功能模块之一——输入捕获…

Python基本运算

1.逻辑运算符 第四行会有黄色的下划线是因为这个不是系统推荐的写法&#xff0c;系统推荐的是第五行的链式比较&#xff1b; 2.短路求值 对于and而言&#xff0c;左边的语句是false&#xff0c;那么整体一定是false,右边的表达式就不会进行计算&#xff1b; 对于or而言&…

ChatGLM3:AttributeError_ can‘t set attribute ‘eos_token‘

最近在微调 ChatGLM3-6b 时&#xff0c;训练好模型之后&#xff0c;调用inference_hf.py函数验证模型的时候报了如下错误&#xff0c;下面是解决方案。 我在训练时使用的是ptuning_v2.yaml配置文件&#xff0c;训练运行代码如下&#xff1a; CUDA_VISIBLE_DEVICES1 python fi…

C++取经之路(其二)——含数重载,引用。

含数重载: 函数重载是指&#xff1a;在c中&#xff0c;在同一作用域&#xff0c;函数名相同&#xff0c;形参列表不相同(参数个数&#xff0c;或类型&#xff0c;或顺序)不同&#xff0c;C语言不支持。 举几个例子&#xff1a; 1.参数类型不同 int Add(int left, int right)…

智慧酒店(一):EasyCVR酒店安防视频监控系统的搭建与特点分析

一、行业背景 随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;已经渗透到我们生活的方方面面&#xff0c;智慧酒店作为现代酒店业的重要发展方向&#xff0c;人工智能的应用显得尤为重要。数据显示&#xff0c;全国智慧酒店每年以10%—15%的速度快速增长&a…

大型DMP系统

前言 大家好&#xff0c;我是jiantaoyab&#xff0c;这是我作为学习笔记总结应用篇第一篇&#xff0c;本章大量的参考了别的博主的文章。 我们今天就先从搭建一个大型的 DMP 系统开始&#xff0c;利用组成原理里面学到的存储器知识&#xff0c;来做选型判断&#xff0c;从而更…

Redis高级面试题-2024

说说你对Redis的理解 Redis是一个基于Key-Value存储结构的开源内存数据库&#xff0c;也是一种NoSQL数据库。 它支持多种数据类型&#xff0c;包括String、Map、Set、ZSet和List&#xff0c;以满足不同应用场景的需求。 Redis以内存存储和优化的数据结构为基础&#xff0c;提…

短视频矩阵系统--技术3年源头迭代

短视频矩阵系统核心技术算法主要包括以下几个方面&#xff1a; 1. 视频剪辑&#xff1a;通过剪辑工具或API从各大短视频平台抓取符合要求的视频。这些视频通常符合某些特定条件&#xff0c;如特定关键词、特定时间段发布的视频、视频点赞评论转发等数据表现良好的视频。 2. 视…

揭露非法集资陷阱!

常见的非法集资手法 犯罪分子利用了社会公众的哪些心理&#xff1f; 使用了怎样的措辞&#xff1f; 一起来揭露非法资金集聚的几个陷阱&#xff01; 拐弯抹角地向亲朋好友承诺大额回报&#xff0c;希望他们加入&#xff08;利用社会认同原则&#xff09;。 不法分子造了个传…