【单例模式】

news2024/12/28 12:53:41

单例模式是指在内存中只会创建且仅创建一次对象的设计模式。

一、实现方式

1. 饿汉式

 在类加载的时候就创建实例,无论是否使用,实例都会被创建。优点是实现简单,线程安全。缺点是可能造成资源浪费,而程序可能不一定会使用这个实例。

代码示例:

public class Singleton {
    //创建实例,并且用static保证实例唯一
    private static final Singleton instance = new Singleton();
   
    //把构造方法设置为private,在类外就无法通过new的方式来创建Singleton实例
    private Singleton() {}
    
    //获取实例
    public static Singleton getInstance() {
        return instance;
    }
}


2. 懒汉式写法

public class Singleton {
    private static Singleton instance = null;

    private Singleton() {}

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

说明:在第一次调用获取实例方法时才创建实例。

思考:上述写的饿汉模式懒汉模式,在多线程环境中是否安全呢?为什么?

分析饿汉模式:

饿汉模式,只涉及到读操作,因此,在多线程环境中是线程安全的。

分析懒汉模式:

懒汉模式,涉及到了读和写操作,会出现脏读的情况,因此,懒汉模式不是线程安全的。 

如图所示:

通过加锁改进刚才代码:

public class Singleton {
    private static Singleton instance = null;

    private Singleton() {

    }

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

说明如下图所示:

刚才代码还有缺陷:分析代码:

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

思考: 

如果每次调用getInstance方法的时候,都会进行加锁操作,加锁操作是有性能开销的,真的需要每次都进行加锁吗?

分析:这里的加锁在new出对象之前加上是有必要的,但是,一旦对象new完了,后续调用getInstance方法,此时instance的值一定是非空的,就会触发return。

解决方案:加上一个条件判断,如果对象没有创建才加锁,否则就return。

代码示例:

public class Singleton {
    private static volatile Singleton instance;
    private Singleton() {}

    public static Singleton getInstance() {
        //这个条件判断的作用是:是否要加锁
        if (instance == null) {  
            synchronized (Singleton.class) {
                
                //这个条件判断的作用是:是否要创建对象
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

你以为就完了吗?

上述代码当中还存在内存可见性和指令重排序的问题。

分析:假设现在有很多线程去调用getInstance方法,只有第一次读的时候才去读内存,后续都是读寄存器或者缓存(此时有如果发生了写操作,其他线程,或者处理器是感知不到的)就会发生内存可见性问题。

指令重排序问题本质上就是编译器或者处理器优化。

instance = new Singleton();这条语句拆分成三个步骤:

1:申请内存空间

2:调用构造方法,把这个内存空间初始化成一个合理的对象

3: 把内存空间的地址赋值给instance引用

 在正常情况下是按照 1 2 3这个顺序去执行,此时此刻,编译器为了提高程序的执行效率,有可能把这个1 2 3 的执行顺序优化成 1 3 2或者其他情况。

如图所示:

 volatile来解决内存可见性和指令重排序问题:

代码示例:

public class Singleton {
   
    //volatile来解决内存可见性和指令重排序问题
    //static实例唯一,共享
    private static volatile Singleton instance;
    
    //禁止外部new对象
    private Singleton() {}

    public static Singleton getInstance() {
        //这个条件判断的作用是:是否要加锁
        if (instance == null) {  
            synchronized (Singleton.class) {
                
                //这个条件判断的作用是:是否要创建对象
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}


3. 静态内部类方式

利用类加载机制实现延迟加载,只有在调用获取实例的方法时,才会加载静态内部类,从而创建实例。线程安全,并且实现简单高效。

public class Singleton {
    private Singleton() {}

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

4. 枚举方式

简洁且线程安全。

public enum SingletonEnum {
    INSTANCE;

    public void doSomething() {
        // 具体方法实现
    }
}

 

二、单例模式的优点

1. 减少系统资源开销:避免了频繁创建和销毁对象带来的资源浪费。

2. 保证对象的唯一性:确保在整个应用程序中只有一个实例存在,方便对这个实例进行统一的管理和控制。

3. 方便资源访问:提供了一个全局访问点,可以方便地获取这个唯一的实例,而不需要在多个地方传递实例对象。

三、单例模式的使用场景

1. 日志记录器:通常整个应用程序只需要一个日志记录器实例,以确保所有的日志信息都被记录到同一个地方。

2. 数据库连接池:管理数据库连接,避免频繁地创建和销毁连接,提高性能。

3. 配置文件读取:应用程序通常只需要一个配置文件读取实例,以确保配置信息的一致性。


 

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

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

相关文章

21 基于51单片机的隧道车辆检测系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 以AT89C51单片机为控制核心,实现对隧道环境的监测。采用模块化设计, 共分以下几个功能模块: 单片机最小系统模块、电源模块、气体传感模块、和显示模块等。 通过…

Yocto - 使用Yocto开发嵌入式Linux系统_08 掌握软件包相关功能

Assimilating Packaging Support 本章介绍了解 Poky 和 BitBake 打包相关方面的关键概念。我们将了解支持的二进制包格式、共享状态缓存、包版本控制组件、如何设置和使用二进制包馈以支持我们的开发流程等。 This chapter presents the key concepts for understanding the as…

Pikachu-Sql-Inject - 通过sql进行远程服务器控制(试验)

secure_file_priv是MySQL中的系统变量,用于限制文件的读取和写入。 查看命令: show variables like "secure%" //或者 select secure_file_priv; 1.secure_file_priv NULL ,限制文件的读取和写入。 2.secure_file_priv 文件路…

第三届图像处理、计算机视觉与机器学习国际学术会议(ICICML 2024)

目录 重要信息 大会简介 组织单位 大会成员 征稿主题 会议日程 参会方式 重要信息 大会官网:www.icicml.org 大会时间:2024年11月22日-24日 大会地点:中国 深圳 大会简介 第三届图像处理、计算机视觉与机器学…

【含开题报告+文档+PPT+源码】基于SpringBoot的社区家政服务预约系统设计与实现【包运行成功】

开题报告 社区家政服务是满足居民日常生活需求的重要组成部分,在现代社会中发挥着越来越重要的作用。随着城市化进程的不断加速,社区家政服务需求量呈现持续增长的趋势。然而,传统的家政服务模式存在一些问题,如预约流程繁琐、信…

Kafka的基本概念整理

1、Kafka是什么? Kafka是由Scala语言开发的一个多分区、多副本,基于Zookeeper集群协调的系统。 那这个所谓的系统又是什么系统呢? 回答这个问题要从发展的角度来看:起初Kafka的定位是分布式消息系统。但是目前它的定位是一个分布…

【M365运维】在SPO文档库里删除文档时,遇到文档被签出无法删除。

【问题】SPO的存储空间剩的不多了,在清理文档库时,遇到有些文档被签出但用户已经离职,删除文件时报错。 【解决】翻SPO的设置时,看到有“管理没有已签入版本的文件”,在里面获取文件的所有权之后就可以删除了。 具体…

如何使用ipopt进行非线性约束求目标函数最小值(NLP非线性规划)内点法(inner point method)

非线性规划,一般用matlab调用cplex和gurobi了,但这两个一般用于线性规划和二次规划 线性规划LP,二次规划(quadratic programming),如果要求更一般的非线性规划IPOT是个很好的选择,求解器很多&a…

点,点间连接的数学构型系统

点,点间连接的数学构型系统 生物神经系统的宇宙时间尺度的进化过程:通过天文数字的生物数量、天文时间尺度的生态系统得自然(按自然规则)的演化,沉淀出最高级的人脑神经系统,那么其实,这个过程…

C嘎嘎入门篇:类和对象番外(时间类)

前文: 小编在前文讲述了类和对象的一部分内容,其中小编讲述过运算符重载这个概念以及一个时间类,当时小编讲的没有那么细致,下面小编将会讲述时间类来帮助各位读者朋友更好的去理解运算符重载,那么,代码时刻…

MySQL 日志 - Binlog

文章目录 binlog 的格式mysqbinlog 工具SHOW binlog events;binlog 和 redo log 对比 https://dev.mysql.com/doc/refman/8.4/en/binary-log.html binlog 全称 BinaryLog,是 MySQL 数据库中用于记录所有更改数据库状态的事件的日志文件。它主要用于以下几个目的&am…

OBOO鸥柏丨OLED透明触摸查询一体机数字科技触控广告屏技术前沿

吊挂透明OLED触摸屏一体机正成为博物馆数字化展示的“共同奔赴赛道选择,透过透明屏幕看到展示物品的内部结构和细节,GG纯平面触控实现展示查询交互与互动的完美结合。相比传统的商用/工业液晶显示屏机柜,OLED透明触摸屏具有更高的对比度和更广…

解锁 Python 嵌套字典的奥秘:高效操作与实战应用指南

文章目录 前言🍀一、 什么是 Python 字典?1.1 字典的语法 🍀二、 字典的基本操作2.1 字典的创建2.2 访问字典中的值2.3 添加或修改键值对2.4 删除字典中的键值对 🍀三、 字典的遍历操作3.1 遍历字典的键3.2 遍历字典的值3.3 同时遍…

UGUI(三大现成UI控件)

Rawimage 可以是任意类型的图,所以这里的泛型就更宽泛,不止sprite 相比Image唯二的不同 uvrect有点像平铺 Text suddenly come to a Free island. best fit开启后会有范围选择 Image image 组件是挂在RectTransform的ui下的,换句话说&…

秋招内推2025-招联金融

【投递方式】 直接扫下方二维码,或点击内推官网https://wecruit.hotjob.cn/SU61025e262f9d247b98e0a2c2/mc/position/campus,使用内推码 igcefb 投递) 【招聘岗位】 后台开发 前端开发 数据开发 数据运营 算法开发 技术运维 软件测试 产品策…

OBOO鸥柏丨户外液晶广告机有哪些功能可以定制室外广告牌?

OBOO鸥柏户外液晶广告机,犹如一位多才多艺的传播使者,巧妙融合信息传播、广告盛宴与互动乐园于一体,室外高亮数字标牌成为都市风景中一抹亮丽的智慧之光。鸥柏液晶户外广告牌轻盈穿梭于高速服务区的喧嚣、智慧城市的新能源充电站、收费站的繁…

Linux_kernel字符设备驱动12

一、字符设备的编程框架 在Linux_kernel驱动开发11中,我们介绍的系统调用。只是为了做一个实验,在真正开发时,我们并不会直接在内核中添加一个新的系统调用,这样做会导致内核体积变大。 1、字符设备结构体 我们实现一个硬件字符设…

WPF|依赖属性SetCurrentValue方法不会使绑定失效, SetValue方法会使绑定失效?是真的吗?

引言 最近因为一个触发器设置的结果总是不起效果的原因,进一步去了解[依赖属性的优先级](Dependency property value precedence - WPF .NET | Microsoft Learn)。在学习这个的过程中发现对SetCurrentValue一直以来的谬误。 在WPF中依赖属性Dependency property的…

力扣 中等 216组合总和III

文章目录 题目介绍解法 题目介绍 解法 是77.组合链接的扩展 class Solution {List<List<Integer>> result new ArrayList<>();List<Integer> path new ArrayList<>();public List<List<Integer>> combinationSum3(int n, int k) …

流速仪设备操作说明

1 流速仪设备安装 按图示对流速仪设备进行安装&#xff0c;主要是流速仪和电频。 连接电脑&#xff0c;直接插上信号接收器&#xff0c;后面使用蓝牙连接。 2 安装软件 3 双击ParaniWin进行设备连接 3.1 按图片所示&#xff0c;设置端口等信息 3.2 Device Setting —>输…