当前位置: 首页 > news >正文

单例模式:确保一个类只有一个实例

目录

引言

1. 单例模式的核心思想

2. 单例模式的实现方式

2.1 饿汉式单例

2.2 懒汉式单例

2.3 线程安全的懒汉式单例

2.4 双重检查锁定(Double-Checked Locking)

2.5 静态内部类实现单例

2.6 枚举实现单例

3. 单例模式的使用场景

4. 单例模式的优缺点

优点:

缺点:

5. 总结


引言

单例模式(Singleton Pattern)是设计模式中最简单且最常用的创建型模式之一。它的核心思想是确保一个类只有一个实例,并提供一个全局访问点来获取该实例。单例模式在许多场景中非常有用,例如配置管理、线程池、数据库连接池等。


1. 单例模式的核心思想

单例模式的核心思想是:

  1. 私有化构造函数:防止外部通过 new 关键字创建实例。

  2. 提供一个静态方法:用于获取类的唯一实例。

  3. 确保唯一性:在整个应用程序生命周期中,类的实例只有一个。


2. 单例模式的实现方式

单例模式有多种实现方式,每种方式都有其优缺点。以下是几种常见的实现方式:

2.1 饿汉式单例

饿汉式单例在类加载时就创建实例,因此是线程安全的。

public class Singleton {// 在类加载时创建实例private static final Singleton INSTANCE = new Singleton();// 私有化构造函数private Singleton() {}// 提供全局访问点public static Singleton getInstance() {return INSTANCE;}
}

饿汉式是最简单的单例模式的写法,保证了线程的安全,在很长的时间里,我都是饿汉模式来完成单例 的,因为够简单,后来才知道饿汉式会有一点小问题,看下面的代码:

 public class Hungry {private byte[] data1 = new byte[1024];private byte[] data2 = new byte[1024];private byte[] data3 = new byte[1024];private byte[] data4 = new byte[1024];private Hungry() {}private final static Hungry hungry = new Hungry();public static Hungry getInstance() {return hungry;}}

 在Hungry类中,我定义了四个byte数组,当代码一运行,这四个数组就被初始化,并且放入内存了,如 果长时间没有用到getInstance方法,不需要Hungry类的对象,这不是一种浪费吗?我希望的是 只有用 到了 getInstance方法,才会去初始化单例类,才会加载单例类中的数据。所以就有了 第二种单例模 式:懒汉式。

优点

  • 实现简单,线程安全。

缺点

  • 如果实例未被使用,会造成资源浪费。


2.2 懒汉式单例

懒汉式单例在第一次调用 getInstance() 时才创建实例。

public class LazyMan {private LazyMan() {System.out.println(Thread.currentThread().getName()+"Start");}private static LazyMan lazyMan;public static LazyMan getInstance() {if (lazyMan == null) {lazyMan = new LazyMan();}return lazyMan;}// 测试并发环境,发现单例失效public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(()->{LazyMan.getInstance();}).start();}}}

缺点

  • 线程不安全,多个线程可能同时进入 if (instance == null) 条件,导致创建多个实例。


2.3 线程安全的懒汉式单例

通过在 getInstance() 方法上加锁,可以解决懒汉式单例的线程安全问题。

public class LazyMan {private LazyMan() {}private static LazyMan lazyMan;public static LazyMan getInstance() {if (lazyMan == null) {synchronized (LazyMan.class) {if (lazyMan == null) {lazyMan = new LazyMan();}}}return lazyMan;}}

保证了线程的安全性,又符合了懒加载,只有在用到的时候,才会去初始化,调用 效率也比较高,但是这种写法在极端情况还是可能会有一定的问题。因为 :

lazyMan = new LazyMan();

不是原子性操作,至少会经过三个步骤:

1. 分配对象内存空间

2. 执行构造方法初始化对象

3. 设置instance指向刚分配的内存地址,此时instance !=null;

由于指令重排,导致A线程执行 lazyMan = new LazyMan();的时候,可能先执行了第三步(还没执行第 二步),此时线程B又进来了,发现lazyMan已经不为空了,直接返回了lazyMan,并且后面使用了返回 的lazyMan,由于线程A还没有执行第二步,导致此时lazyMan还不完整,可能会有一些意想不到的错 误,所以就有了下面一种单例模式。


2.4 双重检查锁定(Double-Checked Locking)

双重检查锁定是一种优化后的线程安全懒汉式单例实现方式。

这种单例模式只是在上面DCL单例模式增加一个volatile关键字来避免指令重排:

 public class LazyMan {private LazyMan() {}private volatile static LazyMan lazyMan;public static LazyMan getInstance() {if (lazyMan == null) {synchronized (LazyMan.class) {if (lazyMan == null) {lazyMan = new LazyMan();}}}return lazyMan;}
}

优点

  • 线程安全,且只有在第一次创建实例时加锁,性能较好。

注意

  • 必须使用 volatile 关键字,防止指令重排序导致的问题。


2.5 静态内部类实现单例

静态内部类实现单例是一种优雅且线程安全的方式。

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

优点

  • 线程安全,且只有在调用 getInstance() 时才会加载 SingletonHolder 类,实现懒加载。


2.6 枚举实现单例

枚举实现单例是《Effective Java》推荐的方式,它天然支持线程安全和防止反射攻击。

public enum Singleton {INSTANCE;public void doSomething() {System.out.println("Doing something...");}
}

优点

  • 线程安全,代码简洁,防止反射和序列化破坏单例。


3. 单例模式的使用场景

单例模式适用于以下场景:

  1. 全局配置管理:例如读取配置文件,确保配置信息全局唯一。

  2. 数据库连接池:确保连接池只有一个实例,避免资源浪费。

  3. 日志管理:确保日志记录器全局唯一。

  4. 线程池:确保线程池的唯一性,避免重复创建线程。


4. 单例模式的优缺点

优点:

  • 节省资源:避免重复创建对象,减少内存开销。

  • 全局访问点:方便对唯一实例的管理和访问。

缺点:

  • 扩展性差:单例类通常难以扩展,因为其构造函数是私有的。

  • 违背单一职责原则:单例类既负责创建实例,又负责业务逻辑。

  • 测试困难:单例类的全局状态可能导致测试困难。


5. 总结

单例模式是一种简单但强大的设计模式,适用于需要全局唯一实例的场景。通过不同的实现方式(如饿汉式、懒汉式、双重检查锁定、静态内部类、枚举等),可以满足不同的需求。

在实际开发中,应根据具体场景选择合适的单例实现方式。如果需要懒加载且线程安全,推荐使用静态内部类或枚举实现;如果需要更高的性能,可以考虑双重检查锁定。

相关文章:

单例模式:确保一个类只有一个实例

目录 引言 1. 单例模式的核心思想 2. 单例模式的实现方式 2.1 饿汉式单例 2.2 懒汉式单例 2.3 线程安全的懒汉式单例 2.4 双重检查锁定&#xff08;Double-Checked Locking&#xff09; 2.5 静态内部类实现单例 2.6 枚举实现单例 3. 单例模式的使用场景 4. 单例模式…...

推荐一个好用的在线文本对比网站 - diffchecker

推荐网址&#xff1a;https://www.diffchecker.com UI设计也很不错&#xff0c;响应也很快&#xff0c;广告少 生成的对比还可以生成在线链接&#xff1a;&#xff08;点击右上角“分享”&#xff09; 可设置过期时间等 我生成的示例&#xff1a;https://www.diffchecker.c…...

学习第八十五行

[capture](parameters) -> return_type {// function body }capture: 捕获列表&#xff0c;指定如何捕获周围作用域中的变量。parameters: 参数列表&#xff0c;与普通函数类似。return_type: 返回类型&#xff0c;可以省略&#xff0c;编译器会自动推断。function body: 函…...

基于Django创建一个WEB后端框架(DjangoRestFramework+MySQL)流程

一、Django项目初始化 1.创建Django项目 Django-admin startproject 项目名 2.安装 djangorestframework pip install djangorestframework 解释: Django REST Framework (DRF) 是基于 Django 框架的一个强大的 Web API 框架&#xff0c;提供了多种工具和库来构建 RESTf…...

【Python 2D绘图】Matplotlib绘图(统计图表)

【Python 2D绘图】Matplotlib绘图&#xff08;统计图表&#xff09; 1. 概述1.1 简介1.2 安装1.3 导入1.4 保存1.5 数据来源1.5.1 Numpy ndarray1.5.2 Pandas DataFrame 1.6 中文显示 2. 基础样式2.1 颜色2.1.1 简称2.1.2 全称 2.2 布局2.2.1 Matplotlib 画布划分2.2.2 绘制子图…...

vue3框架的响应式依赖追踪机制

当存在一个响应式变量于视图中发生改变时会更新当前组件的所以视图显示&#xff0c;但是没有视图中不写这个响应式变量就就算修改该变量也不会修改视图&#xff0c;这是为什么&#xff1f;我们能否可以理解宽泛的理解为vue组件的更新就是视图的更新&#xff0c;单当视图中不存在…...

.Net 6 上传文件接口 文件大小报错整体配置

/// <summary>/// 上传文件/// </summary>/// <param name"file"></param>/// <returns></returns>[HttpPost("UploadifyFile")][RequestSizeLimit(2000 * 1024 * 1024)] // 设置最大请求体大小为 100MBpublic async …...

Git基础之工作原理

基础概念 git本地有三个工作区域&#xff0c;工作目录 Working Directory&#xff0c;暂存区Stage/Index和资源区Repository/Git Directory&#xff0c;如果在加上远程的git仓库就是四个工作区域 四个区域与文件交换的命令之间的关系 WorkSpace&#xff1a;工作区&#xff0c;就…...

小程序 wxml 语法 —— 41列表渲染 - 进阶用法

这一节讲解列表渲染的两个进阶用法&#xff1a; 如果需要对默认的变量名和下标进行修改&#xff0c;可以使用 wx:for-item 和 wx:for-item&#xff1a; 使用 wx:for-item 可以指定数组当前元素的变量名使用 wx:for-index 可以指定数组当前下标的变量名 将 wx:for 用在 标签上&…...

ElasticSearch 入门教程

ElasticSearch 入门教程 ElasticSearch 是一个分布式、可扩展的搜索和分析引擎&#xff0c;基于 Apache Lucene 构建&#xff0c;支持全文检索、结构化查询和聚合分析。本教程将带你深入了解 ElasticSearch 的核心概念、安装配置、常见操作&#xff0c;并提供示例代码&#xf…...

用Python写一个算24点的小程序

一、运行界面 二、显示答案——递归介绍 工作流程&#xff1a; 1. 基本情况&#xff1a;函数首先检查输入的数字列表 nums 的长度。如果列表中只剩下一个数字&#xff0c;它会判断这个数字是否接近 24&#xff08;使用 abs(nums[0] - 24) < 1e-10 来处理浮点数精度问题&…...

分布式网络

分布式网络&#xff08;Distributed Network&#xff09;指的是一种计算机网络架构&#xff0c;其中计算资源&#xff08;计算、存储、数据处理等&#xff09;分布在多个物理或逻辑上的节点上&#xff0c;而不是集中在单一的服务器或数据中心中。这种架构的主要目标是提高系统的…...

忘记dedecms后台超级管理员账号和密码的解决方案

解决方案&#xff1a; 方案一、数据库修改&#xff1a; 1、前提是您能登录到数据库后台&#xff0c;登录MySQL数据库管理工具&#xff08;如phpMyAdmin&#xff09; 2、打开数据库中的 dede_admin 表&#xff0c;找到管理员记录&#xff0c;将 pwd 字段的值改成 f297a57a5a7…...

【Linux学习笔记】Linux基本指令分析和权限的概念

【Linux学习笔记】Linux基本指令分析和权限的概念 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;Linux学习笔记 文章目录 【Linux学习笔记】Linux基本指令分析和权限的概念前言一. 指令的分析1.1 alias 指令1.2 grep 指令1.3 zip/unzip 指…...

Git基础之分支

常用指令 git branch 列出本地所有分支 git branch -r 列出所有远程分支 git branch [branch-name] 新建一个分支&#xff0c;但依然停留在当前分支 git checkout -b [branch] 新建一个分支&#xff0c;并切换到该分支 git merge [branch] 合并指定分支当前分支 git branch -d …...

MAC电脑常用操作

环境&#xff1a;M3芯片 &#xff0c;macOS15.2 &#x1f680; 快捷键 &#x1f5a5;️ 窗口管理 ✅ 退出/进入全屏模式 • 浏览器等应用&#xff1a;⌘ Command Ctrl F ✅ 最小化当前窗口 • ⌘ Command M • &#x1f4a1; 隐藏窗口但保留应用在后台运行 ✅ 关闭当前标…...

【计算机网络】深入解析 HTTP 协议的概念、工作原理和通过 Fiddler 抓包查看 HTTP 请求/响应的协议格式

网络原理— HTTP 1. 什么是HTTP? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议&#xff1a; HTTP 往往是基于传输层的 TCP 协议实现的 (HTTP1.0,HTTP1.1,HTTP2.0 均为TCP,HTTP3基于UDP实现) 我们平时打开一个网站&#xff0c;就是通过HTTP协议来…...

Springboot redis bitMap实现用户签到以及统计,保姆级教程

项目架构&#xff0c;这是作为demo展示使用&#xff1a; Redis config&#xff1a; package com.zy.config;import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.Ob…...

【C++】:STL详解 —— 红黑树封装map和set

目录 红黑树的源代码 正向迭代器的代码 反向迭代器的代码 set的模拟实现 map的模拟实现 红黑树的源代码 #pragma once #include <iostream>using namespace std; // set ->key // map ->key/value// set ->key // map ->key/valueenum Colour {RED,BLAC…...

FPGA设计时序约束用法大全保姆级说明

目录 一、序言 二、时序约束概览 2.1 约束五大类 2.2 约束功能简述 2.3 跨时钟域约束 三、时序约束规范 3.1 时序约束顺序 3.2 约束的优先级 四、约束示例 4.1 设计代码 4.2 时序结果 4.2.1 create_clock 4.2.2 create_generated_clock 4.2.3 Rename_Auto-Derive…...

招商蛇口 | 执笔CID,启幕低密生活新境

作为中国城市生长的力量&#xff0c;招商蛇口以“美好生活承载者”为使命&#xff0c;深耕全球111座城市&#xff0c;以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子&#xff0c;招商蛇口始终与城市发展同频共振&#xff0c;以建筑诠释对土地与生活的…...

OD 算法题 B卷【正整数到Excel编号之间的转换】

文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的&#xff1a;a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...

Python训练营-Day26-函数专题1:函数定义与参数

题目1&#xff1a;计算圆的面积 任务&#xff1a; 编写一个名为 calculate_circle_area 的函数&#xff0c;该函数接收圆的半径 radius 作为参数&#xff0c;并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求&#xff1a;函数接收一个位置参数 radi…...

Android写一个捕获全局异常的工具类

项目开发和实际运行过程中难免会遇到异常发生&#xff0c;系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler&#xff0c;它是Thread的子类&#xff08;就是package java.lang;里线程的Thread&#xff09;。本文将利用它将设备信息、报错信息以及错误的发生时间都…...

ArcPy扩展模块的使用(3)

管理工程项目 arcpy.mp模块允许用户管理布局、地图、报表、文件夹连接、视图等工程项目。例如&#xff0c;可以更新、修复或替换图层数据源&#xff0c;修改图层的符号系统&#xff0c;甚至自动在线执行共享要托管在组织中的工程项。 以下代码展示了如何更新图层的数据源&…...

向量几何的二元性:叉乘模长与内积投影的深层联系

在数学与物理的空间世界中&#xff0c;向量运算构成了理解几何结构的基石。叉乘&#xff08;外积&#xff09;与点积&#xff08;内积&#xff09;作为向量代数的两大支柱&#xff0c;表面上呈现出截然不同的几何意义与代数形式&#xff0c;却在深层次上揭示了向量间相互作用的…...

基于stm32F10x 系列微控制器的智能电子琴(附完整项目源码、详细接线及讲解视频)

注&#xff1a;文章末尾网盘链接中自取成品使用演示视频、项目源码、项目文档 所用硬件&#xff1a;STM32F103C8T6、无源蜂鸣器、44矩阵键盘、flash存储模块、OLED显示屏、RGB三色灯、面包板、杜邦线、usb转ttl串口 stm32f103c8t6 面包板 …...

【记录坑点问题】IDEA运行:maven-resources-production:XX: OOM: Java heap space

问题&#xff1a;IDEA出现maven-resources-production:operation-service: java.lang.OutOfMemoryError: Java heap space 解决方案&#xff1a;将编译的堆内存增加一点 位置&#xff1a;设置setting-》构建菜单build-》编译器Complier...

Excel 怎么让透视表以正常Excel表格形式显示

目录 1、创建数据透视表 2、设计 》报表布局 》以表格形式显示 3、设计 》分类汇总 》不显示分类汇总 1、创建数据透视表 2、设计 》报表布局 》以表格形式显示 3、设计 》分类汇总 》不显示分类汇总...

Continue 开源 AI 编程助手框架深度分析

Continue 开源 AI 编程助手框架深度分析 一、项目简介 Continue 是一个模块化、可配置、跨平台的开源 AI 编程助手框架&#xff0c;目标是让开发者能在本地或云端环境中&#xff0c;快速集成和使用自定义的 LLM 编程辅助工具。它通过支持 VS Code 与 JetBrains 等主流 IDE 插件…...