单例模式(Singleton Pattern):深入解析与应用场景

news2024/12/26 21:46:37

一、什么是单例模式?

单例模式(Singleton Pattern)是一种常用的软件设计模式,其核心目标是确保一个类只有一个实例,并提供一个全局访问点。这种模式在许多场景下都非常有用,可以有效地控制资源的访问和管理。

二、单例模式的实现方式

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

public class Singleton {
    private static Singleton instance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
问题示例:

假设两个线程(线程A和线程B)几乎同时调用 getInstance(),流程如下:

  1. 线程A和线程B 都检查到 if (instance == null)
    • 因为 instance 还未被初始化,所以两个线程都进入了 if 块。
  2. 线程A执行 instance = new Singleton();
    • 线程A完成了对象的创建,instance 不再为 null
  3. 线程B执行 instance = new Singleton();
    • 因为线程B在检查 instance == null 时,instance 仍然为 null(线程A的赋值操作还未被线程B感知)。
    • 线程B再次创建了一个新实例,覆盖了线程A的结果。

最终,多个线程可能创建了多个实例

2. 懒汉式(线程安全)

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

使用 synchronized 关键字确保线程安全:

3. 饿汉式(线程安全)

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

private static final Singleton INSTANCE = new Singleton(); 意味着实例是在类加载时就被创建的
● 这种方式由Java虚拟机保证线程安全,因为静态成员变量的初始化是在类加载过程中进行的,是线程安全的

4. 双重检查锁(推荐)

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;
    }
}

"双重检查锁定”策略来保证 懒加载线程安全,同时避免不必要的同步带来的性能开销。

第一次检查:if (instance == null)
  • 当多个线程并发调用 getInstance() 时,第一次检查是否实例化了 Singleton
  • 如果 instance 已经被创建,直接返回该实例,避免进入同步代码块,从而提高性能。
同步块:synchronized (Singleton.class)
  • 如果 instance 为空,则进入同步块,确保只有一个线程可以执行实例化的代码。
  • 这个 **synchronized** 确保了当多个线程同时进入该代码块时,只有一个线程能够创建 Singleton 实例。
第二次检查:if (instance == null)
  • 线程进入同步块后,第二次检查 instance 是否为空。因为可能多个线程并发到达同步块,第一个线程会创建实例,其他线程会被阻塞。
  • 第二次检查是为了防止多个线程在同步块中创建多个实例,确保 instance 只被初始化一次。
实例化:instance = new Singleton()
  • 当第一次和第二次检查都发现 instance 为空时,创建 Singleton 实例。
为什么使用 volatile 关键字?
  • volatile 确保了 instance 的变量在多线程环境下是可见的,避免了由于 指令重排缓存 导致实例初始化出现问题。
  • 如果没有 volatile,可能会出现某些线程看到一个未完全初始化的 instance 对象,从而导致 空指针异常不一致的状态

5. 静态内部类(推荐)

public class Singleton {
    private Singleton() {}
    
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
为什么这种方式是线程安全的?
  • 类加载机制:Java 类加载是线程安全的。当 JVM 加载类时,它会保证类的初始化过程不会出现竞争条件。因此,SingletonHolder 类在被加载时,INSTANCE 只会被初始化一次,即使在多线程环境下也能保证唯一性。
  • 懒加载:静态内部类实现的单例模式通过 类初始化时 延迟实例化 Singleton,避免了提前创建对象的开销。
  • 性能:由于没有在每次调用 getInstance() 时都加锁,避免了同步带来的性能损失。因此,这种方式在保证线程安全的同时,也具备了更高的性能。

6.使用枚举创建

public enum Singleton {
    INSTANCE;

    // 可以在这里添加需要的方法
    public void doSomething() {
        System.out.println("Doing something...");
    }
}
优点:
  1. enum定义Singleton 被定义为一个枚举类型,只有一个 INSTANCE 成员。枚举类型的实例在类加载时就会被创建,因此可以确保只有一个 INSTANCE
  2. 线程安全:Java 枚举类型天然是线程安全的,因为 Java 在枚举类型初始化时,会确保其在 JVM 中的实例是唯一且线程安全的。
  3. 防止反射破坏单例:与其他单例模式不同,枚举类型能够自动防止反射破坏单例,因为枚举类型的构造方法是 私有的,且枚举实例的生成过程由 Java 虚拟机控制。
  4. 防止序列化破坏单例:Java 序列化机制中,枚举实例会被自动处理,避免了通过反序列化重新创建实例的问题。

三、线程安全的单例实现

实现线程安全的单例模式有多种方法:

  1. 使用synchronized关键字
  2. 使用双重检查锁(volatile + synchronized)
  3. 使用静态内部类
  4. 使用枚举(Java)

四、单例模式的使用场景

1.系统配置管理器:

public class SystemConfigManager {
    private static SystemConfigManager instance;
    private Properties configuration;

    private SystemConfigManager() {
        configuration = new Properties();
        loadConfiguration();
    }

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

    private void loadConfiguration() {
        try (InputStream input = new FileInputStream("config.properties")) {
            configuration.load(input);
        } catch (IOException ex) {
            System.err.println("无法加载配置文件:" + ex.getMessage());
        }
    }

    public String getConfigValue(String key) {
        return configuration.getProperty(key);
    }

    public void setConfigValue(String key, String value) {
        configuration.setProperty(key, value);
        saveConfiguration();
    }

    private void saveConfiguration() {
        try (OutputStream output = new FileOutputStream("config.properties")) {
            configuration.store(output, "系统配置");
        } catch (IOException ex) {
            System.err.println("无法保存配置文件:" + ex.getMessage());
        }
    }
}

2.日志管理

public class Logger {
    private static Logger instance;
    private FileWriter fileWriter;

    private Logger() {
        try {
            fileWriter = new FileWriter("application.log", true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

    public void log(String message) {
        try {
            fileWriter.write(new Date() + ": " + message + "\n");
            fileWriter.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

// 使用示例
public class Application {
    public void performAction() {
        Logger logger = Logger.getInstance();
        logger.log("Action performed successfully");
    }
}

单例模式的典型应用领域

  1. 资源管理:连接池、缓存
  2. 全局状态控制:配置管理、系统设置
  3. 硬件交互:设备管理、外围设备控制
  4. 日志记录:集中式日志管理
  5. 服务协调:线程池、任务调度

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

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

相关文章

【Linux】匿名管道通信场景——进程池

🔥 个人主页:大耳朵土土垚 🔥 所属专栏:Linux系统编程 这里将会不定期更新有关Linux的内容,欢迎大家点赞,收藏,评论🥳🥳🎉🎉🎉 文章目…

C#基础之集合讲解

文章目录 1 集合1.1 数组1.1.1 简介1.1.2 声明使用1.1.2.1 声明 & 初始化1.1.2.2 赋值给数组1.1.2.3 访问数组元素 1.1.3 多维数组1.1.3.1 声明1.1.3.2 初始化二维数组1.1.3.3 访问二维数组元素 1.1.4 交错数组1.1.5 传递数组给函数1.1.6 Array1.1.6.1 简介1.1.6.2 属性1.1…

Azure DevOps Server:使用甘特图Gantt展示需求进度

自从Azure DevOps Server取消与Project Server的集成后,许多用户都在关注如何使用甘特图来展示项目进度。 在Azure DevOps Server开放扩展Extension功能后,许多开发者或专业开发团队做了很多甘特图Gantt相关的开发工作,使用比较多的是(GANTT …

数据湖的概念(包含数据中台、数据湖、数据仓库、数据集市的区别)--了解数据湖,这一篇就够了

文章目录 一、数据湖概念1、企业对数据的困扰2、什么是数据湖3、数据中台、数据湖、数据仓库、数据集市的区别 网上看了好多有关数据湖的帖子,还有数据中台、数据湖、数据仓库、数据集市的区别的帖子,发现帖子写的都很多,而且专业名词很多&am…

Kali Linux怎么开python虚拟环境

相信很多朋友再学习的过程中都会遇到一些pip失效,或者报错的时候,他们要求我们要使用虚拟环境,但是不知道怎么搭建,下面这篇文章就来告诉你如何搭建虚拟环境,这个方法在所有Linux的服务器都通用,就两行命令…

Flink四大基石之State(状态) 的使用详解

目录 一、有状态计算与无状态计算 (一)概念差异 (二)应用场景 二、有状态计算中的状态分类 (一)托管状态(Managed State)与原生状态(Raw State) 两者的…

【数据结构计数排序】计数排序

非比较排序概念 非比较排序是一种排序算法,它不是通过比较元素大小进行排序的,而是基于元素的特征和属性排序。这种排序方法在特定情况下,可以做到比元素比较排序(快排,归并)更有效率。尤其是在处理大量数…

Java GET请求 请求参数在Body中使用Json格式传参

业务需要调个三方接口 使用GET请求方式 但是!请求参数不在Query中,竟然在Body中,使用Json格式传参 在API调试工具里面可以调通 在java代码里,死活调不通 网上搜了搜,找到一个靠谱的,记录一下 import o…

Linux的文件系统

这里写目录标题 一.文件系统的基本组成索引节点目录项文件数据的存储扇区三个存储区域 二.虚拟文件系统文件系统分类进程文件表读写过程 三.文件的存储连续空间存放方式缺点 非连续空间存放方式链表方式隐式链表缺点显示链接 索引数据库缺陷索引的方式优点:多级索引…

[golang][MAC]Go环境搭建+VsCode配置

一、go环境搭建 1.1 安装SDK 1、下载go官方SDK 官方:go 官方地址 中文:go 中文社区 根据你的设备下载对应的安装包: 2、打开压缩包,根据引导一路下一步安装。 3、检测安装是否完成打开终端,输入: go ve…

从繁琐到高效:智能生成PPT的神秘力量

在这个技术爆炸的时代,一场精彩的演讲离不开一份出色的PPT。但制作PPT,就像是一场与时间的博弈,费尽心思构思版式、精炼文案、选择配图,稍不留神,就会被拖入无底深渊。可是你知道吗?现在只需动动手指&#…

二分法篇——于上下边界的扭转压缩间,窥见正解辉映之光(1)

前言 二分法,这一看似简单却又充满哲理的算法,犹如一道精巧的数学之门,带领我们在问题的迷雾中找到清晰的道路。它的名字虽简单,却深藏着智慧的光辉。在科学的浩瀚星空中,二分法如一颗璀璨的星辰,指引着我们…

【软件各类应用解决方案】ERP企业资源管理系统整体解决方案,采购管理方案,仓库管理方案,财务管理方案,人力管理方案,资产管理方案,对账管理(word完整版)

目录 第一部分 概述 第二部分 方案介绍 第三部分 系统业务流程 3.1 关键需求概括分析 3.1.1 销售管理方面 3.1.2 采购管理方面 3.1.3 仓库管理方面 3.1.4 财务管理方面 3.1.5 人力资源方面 3.2 关键需求具体分析 3.2.1 财务管理 3.2.1.1会计凭证解决 3.2.1.2钞…

实验二 选择结构程序设计

实验名称 实验二 选择结构程序设计 实验目的 (1)掌握关系运算符和逻辑运算符的使用方法,能够表达复杂的逻辑条件。 (2)掌握if语句的使用方法,掌握多重条件下的if语句嵌套使用。 (3)…

第33周:运动鞋识别(Tensorflow实战第五周)

目录 前言 一、前期工作 1.1 设置GPU 1.2 导入数据 1.3 查看数据 二、数据预处理 2.1 加载数据 2.2 可视化数据 2.3 再次检查数据 2.4 配置数据集 2.4.1 基本概念介绍 2.4.2 代码完成 三、构建CNN网络 四、训练模型 4.1 设置动态学习率 4.2 早停与保存最佳模型…

Robot Screw Theory (Product of Exponentials)机器人螺旋理论(指数积)

Screw Theory 螺旋理论 Screw theory uses the fact that every rigid body transformation can be expressed by a rotational and translational movement.螺旋理论利用了每个刚体变换都可以通过旋转和平移运动来表示这一事实。 Robert Ball developed the theory in 19th ce…

【maven-5】Maven 项目构建的生命周期:深入理解与应用

1. 生命周期是什么 ​在Maven出现之前,项目构建的生命周期就已经存在,软件开发人员每天都在对项目进行清理,编译,测试及部署。虽然大家都在不停地做构建工作,但公司和公司间,项目和项目间,往往…

skywalking 配置elasticsearch持久化

下载和启动elasticsearch elasticsearch-7.17.25-linux-x86_64.tar.gz,解密文件tar -xvf elasticsearch-7.17.25-linux-x86_64.tar.gz 进入到bin目录,启动 elasticsearch -d 后台运行 下载skywalking服务包 apache-skywalking-apm-9.3.0.tar.gz&#x…

MySQL 复合查询

实际开发中往往数据来自不同的表,所以需要多表查询。本节我们用一个简单的公司管理系统,有三张表EMP,DEPT,SALGRADE 来演示如何进行多表查询。表结构的代码以及插入的数据如下: DROP database IF EXISTS scott; CREATE database IF NOT EXIST…

数据集-目标检测系列- 海边漫步锻炼人检测数据集 person >> DataBall

数据集-目标检测系列- 海边漫步锻炼人检测数据集 person >> DataBall DataBall 助力快速掌握数据集的信息和使用方式,会员享有 百种数据集,持续增加中。 需要更多数据资源和技术解决方案,知识星球: “DataBall - X 数据球…