如何理解单例模式? _

news2024/11/25 20:13:27

例模式(Singleton Pattern):采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。

通俗点来讲:就是一个男人只能有一个老婆,一个女人只能有一个老公

 

单例模式一共有8种方式实现,下面一一举例:

1、饿汉式(静态常量属性)

实现步骤

  1. 构造器私有化
  2. 在类的内部定义静态常量对象
  3. 向外暴露一个静态的公共方法getInstance,返回一个类的对象实例

参考代码

/**
 * Husband,一个女人只能有一个老公husband -> Husband实现单例模式
 * 方式1:饿汉式(静态常量属性)实现单例模式
 */
public class Husband {
    // 第二步:创建私有的静态常量对象
    private static final Husband husband = new Husband();

    // 第一步:构造器私有化
    private Husband() {
    }

    // 第三步:向外提供一个静态的公共方法,以获取该类的对象
    public static Husband getInstance() {
        // 返回的永远是 同一个 husband对象
        return husband;
    }
}

细节说明

  1. 为什么构造器要私有化? -> 使外部无法创建Husband对象,只能使用已经准备好的对象 -> 不能滥情
  2. 为什么属性设置成final? -> 只创建这一个对象husband,以后不会再变 -> 一生只钟情一人
  3. 为什么设置成类变量static? -> 构造器已被私有化,外部无法创建Husband对象,需要类内部提前准备好对象 -> 在类加载时将对象准备好
  4. 为什么公共方法要用static? -> 非static方法属于对象,需要通过对象.方法名()调用 -> 外部类无法创建对象,在没有对象时,外部类无法访问非static方法
  5. 为什么叫饿汉式? -> 只要加载了类信息,对象就已经创建好 -> 只要饿了,就吃东西

优缺点

  • 优点:实现了在整个软件过程中创建一次类的对象,不存在线程安全问题(反射会破坏单例模式安全性),调用效率高
  • 缺点:如果只加载了类,并不需要用到该类对象,对象也已经创建好 -> 存在内存资源浪费问题。

2、饿汉式(静态代码块)

实现步骤

  1. 构造器私有化
  2. 定义静态常量不初始化
  3. 静态代码块中为类属性初始化创建对象
  4. 向外提供一个静态的公共方法,以获取该类的对象

参考代码

/**
 * Husband,一个女人只能有一个老公 -> Husband实现单例模式
 * 方式2:饿汉式(静态代码块)实现单例模式
 */
public class Husband {
    // 第二步:创建私有的静态常量对象
    private static final Husband husband;

    static {
        husband = new Husband();
    }

    // 第一步:构造器私有化 -> 外部无法创建该类对象,只能使用已经准备好的对象
    private Husband() {
    }

    // 第三步:向外提供一个静态的公共方法,以获取该类的对象
    public static Husband getInstance() {
        // 返回的永远是 同一个 husband对象
        return husband;
    }
}

细节说明:原理和第一种饿汉式相同,都是利用类加载时完成对象属性的创建

优缺点同上一种饿汉式

3、懒汉式(线程不安全)

实现步骤

  1. 构造器私有化
  2. 定义一个私有静态属性对象
  3. 提供一个公共的static方法,可以返回一个类的对象

参考代码

/**
 * 一个男人只能有一个老婆wife -> Wife类实现单例模式
 * 方式3:懒汉式单例模式(线程不安全)
 */
public class Wife {
    // 第二步:设置私有静态属性
    private static Wife wife = null;

    // 第一步:构造器私有化
    private Wife() {
    }

    // 第三步:向外提供获取对象的静态方法
    public static Wife getInstance() {
        if (wife == null) { // 如果没有老婆,则分配一个老婆
            wife = new Wife();
        }
        return wife;
    }
}

细节说明

  1. 为什么叫懒汉式? -> 即使加载了类信息,不调用getInstance()方法也不会创建对象 -> 饿了(加载类信息)也不吃东西(不创建对象),懒到一定程度。
  2. 为什么要if判断? -> 防止每次调用时都会重新创建新的对象 -> 防止滥情

优缺点

  • 优点:只有需要对象时才会调用方法返回该类对象,没有则创建对象,避免了资源浪费 -> 实现了延时加载

  • 缺点:线程不安全,分析if代码块:

    if (wife == null) {
        // 当线程1进入if代码块后,还没有完成对象的创建之前,线程2紧随其后也进入了if代码块内
        // 此时就会出现线程1创建了对象,线程2也创建了对象 -> 破坏了单例模式 -> 线程不安全
        wife = new Wife();
        // 通俗的来讲:多个女生看上了同一个男生,问男生有没有老婆?男生回答没有老婆 -> 多个女生先后都当过男生的老婆 -> 前妻太多 -> 违背了单例模式的"一生只钟情一人"的核心思想
    }
    

4、懒汉式(同步代码块)

参考代码

/**
 * 一个男人只能有一个老婆wife -> Wife类实现单例模式
 * 方式4:懒汉式单例模式(同步代码块,线程安全,性能差)
 */
public class Wife {
    // 第二步:设置私有静态属性
    private static Wife wife = null;

    // 第一步:构造器私有化
    private Wife() {
    }

    // 第三步:向外提供获取对象的静态方法
    public static Wife getInstance() {
        synchronized(Wife.class) { // 同步代码块,每个线程进入if判断前都需要获得互斥锁,保证同一时间只有一个线程进入
            if (wife == null) {
                wife = new Wife();
            }
        }
        return wife;
    }
}

说明:给if语句加上synchronized关键字,保证每一次只有一个线程获得互斥锁进入同步代码块,并且将同步代码块全部执行完之后释放锁,切换其他线程执行,类似于数据库中事务的概念(给SQL语句增加原子性),这里是给if语句增加原子性,要么全部执行,要么都不执行。

优缺点

  • 优点:线程安全(反射会破坏安全性)

  • 缺点:性能差 -> 只有第一次创建对象时需要同步代码,确保同一时间只有一个线程进入if语句,后面线程再调用该方法时,对象已经创建好只需要直接返回 -> 每一次线程调用该方法后,都需要等待获取其他线程释放的互斥锁 -> 浪费了大量时间在 等待获取互斥锁 上 -> 效率低下

    通俗的来讲:多个女生问同一个男生有没有老婆? -> 男生回答:需要成为对象才有资格知道(设置同步代码块,线程需要获取互斥锁才能执行代码) -> 每一个女生都需要经过一段长时间的发展,处成对象(线程获取互斥锁) -> 男生告诉自己的对象自己没有老婆(一个线程进入if判断) -> 男生有了老婆(创建对象) -> 返回对象

5、懒汉式(同步方法)

参考代码

/**
 * 一个男人只能有一个老婆wife -> Wife类实现单例模式
 * 方式5:懒汉式单例模式(同步方法,线程安全,性能差)
 */
public class Wife {
    // 第二步:设置私有静态属性
    private static Wife wife = null;

    // 第一步:构造器私有化
    private Wife() {
    }

    // 第三步:向外提供获取对象的静态方法
    public synchronized static Wife getInstance() { // 同步方法,原理和同步代码块实现懒汉式相同
        if (wife == null) {
            wife = new Wife();
        }
        return wife;
    }
}

优缺点:和同步代码块实现懒汉式类似,这里不过多赘述。

6、懒汉式(DCL模式⭐)

DCL模式实现懒汉式单例模式,即双重检查机制(DCL, Double Check Lock),线程安全,性能高 <- 面试重点

参考代码

/**
 * 一个男人只能有一个老婆wife -> Wife类实现单例模式
 * 方式6:懒汉式单例模式(DCL模式 -> 双重检查,线程安全,性能高)
 */
public class Wife {
    // 第二步:设置私有静态属性
    // volatile关键字:极大程度上避免JVM底层出现指令重排情况,极端情况除外
    private static volatile Wife wife = null;

    // 第一步:构造器私有化
    private Wife() {
    }

    // 第三步:向外提供获取对象的静态方法
    public static Wife getInstance() {
        // 第一层if判断作用:当对象已经创建好时,直接跳过if语句,返回已经创建好的对象,不在等待获取互斥锁 -> 节省时间,提高性能
        if (wife == null) {
            // 注意:这里容易有多个线程同时进入第一层if的代码块中,等待获取对象锁
            synchronized (Wife.class) { // 同步代码块,保证每个线程进入if判断前都需要获得互斥锁
                // 第二层if判断作用:当有多个线程都进入了第一层if语句内,会出现线程1进入时对象为空,则创建对象,释放互斥锁,线程2获得互斥锁后如果没有第二层if判断,则直接创建对象,破坏了单例模式 -> 第二层if保证线程安全
                if (wife == null) {
                    wife = new Wife();
                    // 在JVM底层创建对象时,大致分为3条指令
                    // 1.分配内存空间 -> 2.构造器初始化 -> 3.对象引用指向内存空间
                    // JVM为了执行效率,会打乱指令顺序(指令重排),有可能是1 -> 3 -> 2
                    // 当执行到3时,对象还没有创建完成,但是其他线程在第一层if判断已经创建好对象直接返回,显然不合理(对象属性还没有初始化完成) -> 保证指令执行顺序不被打乱(保证单条语句编译后的原子性) -> 使用volatile变量,禁止JVM优化重排指令
                }
            }
        }
        return wife;
    }
}

DCL模式的两层if判断的作用:

  • 第一层if:已经创建好对象时直接返回,不再排队获取互斥锁,提升效率
  • 第二层if:保证线程安全

通俗的来讲:

  1. 有多个女生问同一个男生有没有老婆?

  2. -> 男生口头回答说没有老婆(进入第一层if判断,如果有老婆则直接远离:不要去碰一个已婚的男人,他是一个女人的余生,不是你的男人不要情意绵绵) -> 其中一个女生和男生处成对象(一个线程获取到互斥锁) -> 经过发展后,女生和男生登记结婚,民政局办理结婚证时检查男生婚姻情况(第二层if判断) --未婚--> 成为夫妻,男生获得老婆

  3. 获得老婆信息

优缺点

  • 优点:性能高,线程安全,延时加载
  • 缺点:由于JVM底层模型,volatile不能完全避免指令重排的情况,会偶尔出现问题,反射、序列化会破坏双检索单例。

7、懒汉式(静态内部类)

参考代码

/**
 * 一个男人只能有一个老婆wife -> Wife类实现单例模式
 * 方式7:懒汉式单例模式(静态内部类,线程安全,性能高)
 */
public class Husband {
    private Husband() {}

    private static class Wife {
        private static Husband husband = new Husband();
    }

    public static Husband getInstance() {
        return Wife.husband;
    }
}

细节说明

  1. 静态内部类实现的单例模式同样是懒汉式 -> 外部类加载时并没有创建好对象,只有调用特定方法时才会加载静态内部类信息(内部类的静态对象属性创建完毕)
  2. 静态内部类的方式实现的单例模式:线程安全(只有在第一次加载内部类信息时才会创建对象),效率高(不需要获取互斥锁)

优缺点

  • 优点:线程安全,效率高,实现了延迟加载
  • 缺点:只适合简单的对象实例,需要创建的对象实例有复杂操作时(如要对 对象实例 进行其他赋值操作),代码会更复杂。反射会破坏单例模式。

8、饿汉式(枚举) 

参考代码

enum Husband {
    HUSBAND;
}

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

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

相关文章

【流量卡奸商自白书】:坑很多,多多少少你得跳一个!

大家好&#xff0c;今天这期短文咱们来讲讲流量卡奸商的自白书&#xff0c;打破你的认知&#xff01;话说虽然没有胆子虚假宣传&#xff0c;没有胆子不给售后&#xff0c;但是呢&#xff0c;整活忽悠消费者来办卡的胆子还是有的&#xff0c;而且还不小。 ​ 作为一个流量卡奸商…

ALLEGRO之Help

本文主要介绍ALLEGRO的Help菜单。 &#xff08;1&#xff09;Documentation&#xff1a;弹出帮助文档&#xff1b; &#xff08;2&#xff09;Whats New&#xff1a;介绍新特性&#xff1b; &#xff08;3&#xff09;Search&#xff1a;暂不清楚&#xff1b; &#xff08;4…

面试之多线程案例(四)

1.单例模式 单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时&#xff0c;为了防止频繁地创建对象使得内存飙升&#xff0c;单例模式可以让程序仅在内存中创建一个对象&#xff0c;让所有需要调用的地方都共享这一单例对象。…

SSL原理详解

SSL协议结构&#xff1a; SSL协议分为两层&#xff0c;下层为SSL记录协议&#xff0c;上层为SSL握手协议、SSL密码变化协议和SSL警告协议。 1.下层为SSL记录协议&#xff0c;主要作用是为高层协议提供基本的安全服务 建立在可靠的传输之上&#xff0c;负责对上层的数据进行分块…

HDFS集群黑白名单机制

HDFS集群黑白名单机制 白名单黑名单 白名单 所谓白名单指的是允许哪些机器加入到当前的HDFS集群中&#xff0c;是一种准入机制白名单由dfs.hosts参数指定&#xff0c;该参数位于hdfs-site.xml.默认值为空dfs.hosts只想文件&#xff0c;该文件包含允许链接到namanode的主机列表…

OpenCVForUnity(九)图片模糊

文章目录 前言一、归一化框滤波器使用blur方法来实现 二、高斯滤波器使用GaussianBlur方法实现 三、中值滤波器使用medianBlur方法实现 四、双边过滤器使用bilateralFilter方法实现 结语 前言 本教程将介绍使用OpenCV中的多种线性滤波器来对图像进行平滑处理&#xff0c;主要包…

程序员面试IT技术岗的三大技巧

文章目录 技巧一&#xff1a;深入研究意向企业技巧二&#xff1a;准备常见的面试问题技巧三&#xff1a;总结经历的面试题 在找工作时&#xff0c;面试是每位程序员必须经历的一关。面对众多求职者竞争激烈的情况&#xff0c;我们需要结合自己的现状&#xff0c;针对意向企业做…

复习第三章反射+IO流

一、反射 JAVA反射机制是在运行状态中&#xff0c;对于任意一个类&#xff0c;都能够知道这个类的所有属性和方法&#xff1b;对于任意一个对象&#xff0c;都能够调用它的任意一个方法和属性&#xff1b;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机…

在使用Python爬虫时遇到503 Service Unavailable错误解决办法汇总

在进行Python爬虫的过程中&#xff0c;有时会遇到503 Service Unavailable错误&#xff0c;这意味着所请求的服务不可用&#xff0c;无法获取所需的数据。为了解决这个常见的问题&#xff0c;本文将提供一些解决办法&#xff0c;希望能提供实战价值&#xff0c;让爬虫任务顺利完…

机器人科普--AGILOX 叉车

机器人科普--AGILOX 叉车 1 概述2 导航3 驱动轮组4 叉举参考 1 概述 AGILOX 叉车&#xff0c;不需要画地图路径&#xff0c;很厉害。 2 导航 中间路径自由导航&#xff0c;末端规划出轨迹路线&#xff0c;并使用优良的控制器做轨迹追踪。 AGILOX &#xff5c; 10 Min setu…

mysql安装教程保姆级

MySQL免安装本地运行 1.下载MySQL2.创建install.bat3.init.sql 初始创建4.环境变量配置5.运行 install.bat 管理员权限运行6.连接成功遇到的问题 1.下载MySQL ①地址&#xff1a;https://downloads.mysql.com/archives/community/ ②解压 2.创建install.bat 放在mysql>b…

时序预测 | MATLAB实现GRNN广义回归神经网络时间序列预测(多指标,多图)

时序预测 | MATLAB实现GRNN广义回归神经网络时间序列预测(多指标,多图) 目录 时序预测 | MATLAB实现GRNN广义回归神经网络时间序列预测(多指标,多图)效果一览基本介绍程序设计参考资料效果一览 基本介绍 1.MATLAB实现GRNN广义回归神经网络时间序列预测(完整源码和数据) …

hive-date

current_date -- 获取当前系统时间 &#xff0c;获取到的是10 的系统时间 例如 &#xff1a; 2023-07-28 select current_date form edw.test;current_timestamp -- 获取当前的时间戳日期格式&#xff0c;例如 1957-07-27 13:31:21.641 会取到毫秒的一个时间戳格式 select *…

openvino批量推理资料汇总

1、开发者实战 | 基于 C# 和 OpenVINO™ 2023.0部署 YOLOv8 全系列模型_OpenVINO 中文社区的博客-CSDN博客 2、在英特尔 CPU 上加速 Stable Diffusion 推理_OpenVINO 中文社区的博客-CSDN博客 3、 5周年更新 | OpenVINO™ 2023.0&#xff0c;让AI部署和加速更容易_OpenVINO 中…

【C++】—— 多态的基本介绍

前言&#xff1a; 在之前的学习过程中&#xff0c;我们已经对继承进行了详细的学习和了解。今天&#xff0c;我将带领大家学习的是关于 多态 的基本知识。 目录 &#xff08;一&#xff09;多态的概念 1、概念 &#xff08;二&#xff09;多态的定义及实现 1、多态的构成条…

【力扣每日一题】2023.8.1 英雄的力量

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目给我们一个数组&#xff0c;让我们找出数组的每个非空子数组&#xff08;不用连续&#xff09;&#xff0c;然后按照公式算出子数组的…

CNN、数据预处理、模型保存

目录 CNN代码读取数据搭建CNN训练网络模型 数据增强迁移学习图像识别策略数据读取定义数据预处理操作冻结resnet18的函数把模型输出层改成自己的设置哪些层需要训练设置优化器和损失函数训练开始训练再训练所有层关机了&#xff0c;再开机&#xff0c;加载训练好的模型 CNN 代码…

计算机网络(2) --- 网络套接字UDP

计算机网络&#xff08;1&#xff09; --- 网络介绍_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/131967378?spm1001.2014.3001.5501 目录 1.端口号 2.TCP与UDP协议 1.TCP协议介绍 1.TCP协议 2.UDP协议 3.理解 2.网络字节序 发送逻辑…

Node.js之express框架学习心得

Node.js:颠覆传统的服务器端开发 Node.js是基于Chrome V8引擎构建的JavaScript运行时,它采用了完全不同的开发模型。Node.js使用事件驱动和非阻塞I/O的方式处理请求,通过单线程和异步机制,实现高效的并发处理。这意味着在Node.js中,一个线程可以处理数千个并发连接,大大提…

Debian 12.1 “书虫 “发布,包含 89 个错误修复和 26 个安全更新

导读Debian 项目今天宣布&#xff0c;作为最新 Debian GNU/Linux 12 “书虫 “操作系统系列的首个 ISO 更新&#xff0c;Debian 12.1 正式发布并全面上市。 Debian 12.1 是在 Debian GNU/Linux 12 “书虫 “发布六周后推出的&#xff0c;目的是为那些希望在新硬件上部署操作系统…