深入浅出 - 单例模式

news2024/9/28 3:24:15

博主介绍: ✌博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家✌

Java知识图谱点击链接:体系化学习Java(Java面试专题)

💕💕 感兴趣的同学可以收藏关注下不然下次找不到哟💕💕

在这里插入图片描述

文章目录

  • 1、什么是单例模式
  • 2、单例模式有多少种写法
  • 3、饿汉式单例模式
  • 4、 懒汉式单例模式
  • 5、双重检查锁单例模式
  • 6、静态内部类单例模式
  • 7、枚举单例模式
  • 8、哪些写法是完美的单例写法

1、什么是单例模式

单例模式是一种创建型设计模式,它保证一个类只有一个实例,并提供一个全局访问点。在单例模式中,该类的构造函数必须是私有的,以防止其他代码创建该类的实例。而且,该类必须提供一个静态方法来获取其唯一实例。

单例模式常用于需要共享资源的情况,例如数据库连接池、线程池、配置文件等。由于只有一个实例存在,因此可以避免资源的浪费和冲突。

单例模式有多种实现方式,例如饿汉式、懒汉式、双重检查锁等。其中,饿汉式在类加载时就创建了唯一实例,因此线程安全,但可能会浪费资源。懒汉式在第一次使用时才创建唯一实例,避免了资源浪费,但需要考虑线程安全问题。双重检查锁则结合了两种方式的优点,既能保证线程安全,又能避免资源浪费。

2、单例模式有多少种写法

单例模式有以下几种常见的实现方式:

  1. 饿汉式单例模式:在类加载时就创建唯一实例,线程安全,但可能会浪费资源。

  2. 懒汉式单例模式:在第一次使用时才创建唯一实例,避免了资源浪费,但需要考虑线程安全问题。

  3. 双重检查锁单例模式:结合了饿汉式和懒汉式的优点,既能保证线程安全,又能避免资源浪费。

  4. 静态内部类单例模式:利用静态内部类的特性,在类加载时不会创建唯一实例,只有在第一次使用时才创建,既能保证线程安全,又能避免资源浪费。

  5. 枚举单例模式:利用枚举类型的特性,保证只有一个实例存在,线程安全,且代码简洁。

以上是常见的几种单例模式的实现方式,每种实现方式都有其优缺点,需要根据具体情况选择适合的实现方式。

3、饿汉式单例模式

饿汉式单例模式是一种单例模式的实现方式。在该实现方式中,单例对象在类加载时就被创建了,因此也被称为“饿汉式”(即“饥不择食”的意思)。具体来说,饿汉式单例模式的实现方式是将单例对象声明为静态变量,并在类加载时就进行初始化,这样在后续使用时就可以直接获取该静态变量的值,无需再创建新的实例。由于该实现方式在类加载时就创建了单例对象,因此不存在线程安全问题。

饿汉式的代码如下:

package com.pany.camp.design.principle;

/**
 *
 * @description: 饿汉式单例模式
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-26 21:39
 */
public class Singleton {

    // 在类加载时就创建唯一实例
    private static Singleton instance = new Singleton();

    // 将构造函数设为私有,防止其他代码创建实例
    private Singleton() {
    }

    // 提供静态方法获取唯一实例
    public static Singleton getInstance() {
        return instance;
    }
}

4、 懒汉式单例模式

懒汉式单例模式是一种单例模式的实现方式。在该实现方式中,单例对象在第一次使用时才被创建,因此也被称为“懒汉式”。具体来说,懒汉式单例模式的实现方式是将单例对象的初始化延迟到第一次使用时,而不是在类加载时就进行初始化。在第一次使用时,如果该单例对象还没有被创建,则创建一个新的实例并返回,否则直接返回已经存在的实例。由于该实现方式在第一次使用时才创建单例对象,因此存在线程安全问题,需要进行相应的线程安全处理,例如使用 synchronized 关键字或者双重检查锁等方式。

代码如下:

package com.pany.camp.design.principle;

/**
 *
 * @description:  懒汉式单例模式
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-26 21:43
 */
public class Singleton1 {

    // 声明静态的、私有的单例变量
    private static Singleton1 instance;

    // 将构造函数设为私有,防止其他代码创建实例
    private Singleton1() {
    }

    // 提供静态方法获取唯一实例
    public static synchronized Singleton1 getInstance() {
        if (instance == null) {
            instance = new Singleton1();
        }
        return instance;
    }
}

懒汉式单例模式的主要问题是线程安全。在多线程环境下,如果多个线程同时调用 getInstance() 方法,可能会导致创建多个实例,从而违反了单例模式的原则。具体来说,如果一个线程在检查 instancenull 时被挂起了,而另一个线程在此时也调用了 getInstance() 方法并创建了实例,那么当第一个线程恢复执行时,它也会创建一个新的实例,从而导致多个实例的产生。为了解决这个问题,可以使用 synchronized 关键字或者双重检查锁等方式来保证线程安全,但这些方式会带来一定的性能损失。

5、双重检查锁单例模式

双重检查锁单例模式是一种单例模式的实现方式,它通过双重检查来保证线程安全和性能。具体来说,该实现方式在第一次使用时才创建单例对象,并使用双重检查来确保在多线程环境下只创建一个实例。

代码如下:

package com.pany.camp.design.principle;

/**
 *
 * @description:  双重检查锁单例模式
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-26 21:45
 */
public class Singleton3 {
    
    // 声明静态的、私有的单例变量
    private static volatile Singleton3 instance;

    // 将构造函数设为私有,防止其他代码创建实例
    private Singleton3() {
    }

    // 提供静态方法获取唯一实例
    public static Singleton3 getInstance() {
        if (instance == null) {
            synchronized (Singleton3.class) {
                if (instance == null) {
                    instance = new Singleton3();
                }
            }
        }
        return instance;
    }
}

Singleton3 类的构造函数被设为私有,防止其他代码创建该类的实例。 instance 静态变量被声明为私有的、静态的、volatile 的,它的初始值为 null 。 getInstance() 方法提供了获取唯一实例的静态方法,该方法使用了双重检查锁来保证线程安全和性能。在方法内部,首先检查 instance 是否为 null ,如果是,则进入同步块。在同步块内部,再次检查 instance 是否为 null ,如果是,则创建一个新的 Singleton3 实例并将其赋值给 instance 。由于使用了双重检查锁,因此可以避免在多线程环境下创建多个实例的问题,并且也可以减少同步的开销。需要注意的是, instance 变量必须声明为 volatile,以确保在多线程环境下的可见性。

6、静态内部类单例模式

静态内部类单例模式是一种单例模式的实现方式,它使用静态内部类来实现单例对象的延迟初始化,从而避免了线程安全问题和性能问题。具体来说,该实现方式在静态内部类中声明单例对象,当外部类第一次使用单例对象时,才会加载静态内部类并创建单例对象。

代码如下:

package com.pany.camp.design.principle;

/**
 *
 * @description: 静态内部类单例模式
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-26 21:47
 */
public class Singleton4 {

    // 将构造函数设为私有,防止其他代码创建实例
    private Singleton4() {
    }

    // 静态内部类,用于延迟初始化单例对象
    private static class SingletonHolder {
        private static final Singleton4 INSTANCE = new Singleton4();
    }

    // 提供静态方法获取唯一实例
    public static Singleton4 getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

Singleton4 类的构造函数被设为私有,防止其他代码创建该类的实例。 SingletonHolder 静态内部类用于延迟初始化单例对象,其中 INSTANCE 静态变量被声明为 final ,并在静态初始化块中创建了一个新的 Singleton 实例。 getInstance() 方法提供了获取唯一实例的静态方法,该方法直接返回 SingletonHolder 中的 INSTANCE 变量。由于静态内部类只会在第一次使用时加载,因此可以保证单例对象的延迟初始化,并且由于静态内部类的加载是线程安全的,因此可以避免线程安全问题。同时,由于单例对象的创建是在静态初始化块中进行的,因此也可以避免性能问题。

7、枚举单例模式

枚举单例模式是一种单例模式的实现方式,它使用枚举类型来实现单例对象的创建。在 Java 中,枚举类型是线程安全的,且只会被初始化一次,因此可以保证枚举单例模式的线程安全性和单例性。

代码如下:

package com.pany.camp.design.principle;

/**
 *
 * @description: 枚举单例模式
 * @copyright: @Copyright (c) 2022 
 * @company: Aiocloud
 * @author: pany
 * @version: 1.0.0 
 * @createTime: 2023-06-26 21:49
 */
public enum Singleton5 {
    
    INSTANCE;
    // 添加其他方法和属性
}

Singleton5 枚举类型只有一个枚举常量 INSTANCE ,它是一个单例对象。由于枚举类型只会被初始化一次,因此可以保证 INSTANCE 的单例性。同时,由于枚举类型是线程安全的,因此可以保证 INSTANCE 的线程安全性。需要注意的是,由于枚举类型不能被继承,因此枚举单例模式不能被子类化,但是可以添加其他方法和属性。

8、哪些写法是完美的单例写法

静态内部类单例模式 、枚举单例模式是常用的完美的单例写法。实际项目中我用的最多的就是

在这里插入图片描述

💕💕 本文由激流原创,首发于CSDN博客,博客主页 https://blog.csdn.net/qq_37967783?spm=1010.2135.3001.5421
💕💕喜欢的话记得点赞收藏啊

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

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

相关文章

【MySQL】幽深不可测,登此方觉心

详解Mysql安装教程 一、MySQL基础 1、MySQL是什么? MySQL是一种用关系型数据库管理系统的软件。它是一种开源数据库,可以利用它来存储、管理和访问各种类型的数据。MySQL可用于多种应用程序,包括网站、电子商务系统、移动应用程序、企业级应用程序等…

Clickhouse物化视图原理和使用详解

前言 ClickHouse广泛用于用户和系统日志查询场景中,主要针对于OLAP场景,为业务方提供稳定高效的查询服务。在业务场景下,数据以不同的格式、途径写入到clickhouse。用传统JOIN方式查询海量数据,通常有如下痛点: 每个查询的代码冗…

【Java】ConcurrentHashMap1.8源码保姆级解析

目录 ConcurrentHashMap 1.8的优化 初始化流程 扩容流程 读取数据流程 计数器的实现 ConcurrentHashMap 1.8的优化 存储结构的优化 数组链表 -> 数组链表红黑树 写数据加锁的优化 扩容的优化(协助优化) 计数器的优化 LongAddr -> Cell[] 分…

力扣算法练习(二)

主要参考自力扣官网解法 1.最长回文子串(5) 给你一个字符串 s,找到 s 中最长的回文子串。 如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。 示例 1: 输入:s "babad" 输出:"bab"…

【Java】JVM学习(七)

JVM调优 堆空间如何设置 在分代模型中,各分区的大小对GC的性能影响很大。如何将各分区调整到合适的大小,分析活跃数据的大小是很好的切入点。 活跃数据的大小:应用程序稳定运行时长期存活对象在堆中占用的空间大小,也就是Full …

Git安装及使用图文教程详解(附带安装文件)

Git安装及使用图文教程详解(附带安装文件) 原创:丶无殇  2023-06-26 文章目录 下载安装下载安装验证安装成功版本查看 基础指令Git常用指令【首次必须】设置签名用户、邮箱1.初始化本地仓库2.查看本地库状态3.创建文件4.添加文件至暂存区5…

【DCT变换】Python矩阵运算实现DCT变换

一、前言 DCT变换(离散余弦变换) 是数字图像处理过程中广泛采用的一种操作,用于将空域的图像转换为频域表示,从而能够更有效地进行压缩、滤波和特征提取等处理。它在许多应用领域中发挥着重要的作用,尤其在图像和视频…

感知机(Perceptron)的原理及实现

1.感知机(Perceptron)的原理及实现 声明:笔记来源于《白话机器学习的数学》 感知机是接受多个输入后将每个值与各自权重相乘,最后输出总和的模型。 单层感知机因过于简单,无法应用于实际问题,但它是神经网络…

一文让你搞定接口测试

一、什么是接口测试? 所谓接口,是指同一个系统中模块与模块间的数据传递接口、前后端交互、跨系统跨平台跨数据库的对接。而接口测试,则是通过接口的不同情况下的输入,去对比输出,看看是否满足接口规范所规定的功能、…

二叉树知识小结

思维导图: 一,树 树,这是一种对计算机里的某种数据结构的形象比喻。比如这种: 这种: 这种: 这几种都是树形结构。在百度百科中对树形结构的定义如下: 树形结构指的是数据元素之间存在着“一对多”的树形关系…

津津乐道设计模式 - 建造者模式详解(教你如何构造一个专属女友)

🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志 🎐 个人CSND主页——Micro麦可乐的博客 🐥《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程,入门到实战 🌺《RabbitMQ》…

接口测试断言详解(Jmeter)

接口测试是目前最主流的自动化测试手段,它向服务器发送请求,接收和解析响应结果,通过验证响应报文是否满足需求规约来验证系统逻辑正确性。接口的响应类型通过Content-Type指定,常见的响应类型有: • text/html &…

Android Jetpack Compose之轻松添加分隔线:Divider组件

引言: 在构建用户界面时,有效地组织和分隔内容是至关重要的。这就是Android Jetpack Compose的Divider组件派上用场的地方。在这篇博客中,我们将详细了解Divider组件的功能和用法,并通过示例展示如何将其融入您的Compose UI。 Je…

自动化测试和性能测试面试题精选

自动化测试相关 包含 Selenium、Appium 和接口测试。 1. 自动化代码中,用到了哪些设计模式? 单例模式工厂模式PO模式数据驱动模式 2. 什么是断言? 检查一个条件,如果它为真,就不做任何事,用例通过。如果…

8年资深测试总结,自动化测试成功实施,你不知道的都在这...

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 什么项目&#xf…

python:并发编程(二十七)

前言 本文将和大家一起探讨python并发编程的实际项目:Locust性能测试(篇一,共N篇),系列文章将会从零开始构建项目,并逐渐完善项目,最终将项目打造成适用于高并发场景的应用。 本文为python并发…

分支定价算法求解VRPTW问题(代码非原创)

参考文献:微信公众号“程序猿声”关于分支定价求解VRPTW的代码 A tutorial on column generation and branch-and-price for vehicle routing problems 框架 对于VRPTW问题,先做线性松弛,调用列生成算法(一种解决大型线性规划问…

Docker网络之Network Namespace

Docker网络中相关的命令非常少,但需要掌握的底层原理却又非常多。 1.Network Namespace Docker网络底层原理是Linux的Network Namespace,所以说对于Linux Network Namespace的理解对Docker网络底层原理的理解就显得尤为重要了。 2.需求 通过手工的方式…

ICC2与INNOVUS命令对照表

ICC2与INNOVUS命令对照表 TargetICC2INNOVUS设置多CPU set_host_options -max_cores16 setMultiCpuUsage -localCpu 16 获得物体的属性 get_attribute

DSP,国产C2000横空出世,QX320F280049,替代TI 的 TMS320F280049,支持国产

一、特性参数 1、独立双核,32位CPU,单核主频400MHz 2、IEEE 754 单精度浮点单元 (FPU) 3、三角函数单元 (TMU) 4、1MB 的 FLASH (ECC保护) 5、1MB 的 SRAM (ECC保护&…