单例模式:确保一个类只有一个实例
目录
引言
1. 单例模式的核心思想
2. 单例模式的实现方式
2.1 饿汉式单例
2.2 懒汉式单例
2.3 线程安全的懒汉式单例
2.4 双重检查锁定(Double-Checked Locking)
2.5 静态内部类实现单例
2.6 枚举实现单例
3. 单例模式的使用场景
4. 单例模式的优缺点
优点:
缺点:
5. 总结
引言
单例模式(Singleton Pattern)是设计模式中最简单且最常用的创建型模式之一。它的核心思想是确保一个类只有一个实例,并提供一个全局访问点来获取该实例。单例模式在许多场景中非常有用,例如配置管理、线程池、数据库连接池等。
1. 单例模式的核心思想
单例模式的核心思想是:
-
私有化构造函数:防止外部通过
new关键字创建实例。 -
提供一个静态方法:用于获取类的唯一实例。
-
确保唯一性:在整个应用程序生命周期中,类的实例只有一个。
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. 单例模式的使用场景
单例模式适用于以下场景:
-
全局配置管理:例如读取配置文件,确保配置信息全局唯一。
-
数据库连接池:确保连接池只有一个实例,避免资源浪费。
-
日志管理:确保日志记录器全局唯一。
-
线程池:确保线程池的唯一性,避免重复创建线程。
4. 单例模式的优缺点
优点:
-
节省资源:避免重复创建对象,减少内存开销。
-
全局访问点:方便对唯一实例的管理和访问。
缺点:
-
扩展性差:单例类通常难以扩展,因为其构造函数是私有的。
-
违背单一职责原则:单例类既负责创建实例,又负责业务逻辑。
-
测试困难:单例类的全局状态可能导致测试困难。
5. 总结
单例模式是一种简单但强大的设计模式,适用于需要全局唯一实例的场景。通过不同的实现方式(如饿汉式、懒汉式、双重检查锁定、静态内部类、枚举等),可以满足不同的需求。
在实际开发中,应根据具体场景选择合适的单例实现方式。如果需要懒加载且线程安全,推荐使用静态内部类或枚举实现;如果需要更高的性能,可以考虑双重检查锁定。
相关文章:
单例模式:确保一个类只有一个实例
目录 引言 1. 单例模式的核心思想 2. 单例模式的实现方式 2.1 饿汉式单例 2.2 懒汉式单例 2.3 线程安全的懒汉式单例 2.4 双重检查锁定(Double-Checked Locking) 2.5 静态内部类实现单例 2.6 枚举实现单例 3. 单例模式的使用场景 4. 单例模式…...
推荐一个好用的在线文本对比网站 - diffchecker
推荐网址:https://www.diffchecker.com UI设计也很不错,响应也很快,广告少 生成的对比还可以生成在线链接:(点击右上角“分享”) 可设置过期时间等 我生成的示例:https://www.diffchecker.c…...
学习第八十五行
[capture](parameters) -> return_type {// function body }capture: 捕获列表,指定如何捕获周围作用域中的变量。parameters: 参数列表,与普通函数类似。return_type: 返回类型,可以省略,编译器会自动推断。function body: 函…...
基于Django创建一个WEB后端框架(DjangoRestFramework+MySQL)流程
一、Django项目初始化 1.创建Django项目 Django-admin startproject 项目名 2.安装 djangorestframework pip install djangorestframework 解释: Django REST Framework (DRF) 是基于 Django 框架的一个强大的 Web API 框架,提供了多种工具和库来构建 RESTf…...
【Python 2D绘图】Matplotlib绘图(统计图表)
【Python 2D绘图】Matplotlib绘图(统计图表) 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框架的响应式依赖追踪机制
当存在一个响应式变量于视图中发生改变时会更新当前组件的所以视图显示,但是没有视图中不写这个响应式变量就就算修改该变量也不会修改视图,这是为什么?我们能否可以理解宽泛的理解为vue组件的更新就是视图的更新,单当视图中不存在…...
.Net 6 上传文件接口 文件大小报错整体配置
/// <summary>/// 上传文件/// </summary>/// <param name"file"></param>/// <returns></returns>[HttpPost("UploadifyFile")][RequestSizeLimit(2000 * 1024 * 1024)] // 设置最大请求体大小为 100MBpublic async …...
Git基础之工作原理
基础概念 git本地有三个工作区域,工作目录 Working Directory,暂存区Stage/Index和资源区Repository/Git Directory,如果在加上远程的git仓库就是四个工作区域 四个区域与文件交换的命令之间的关系 WorkSpace:工作区,就…...
小程序 wxml 语法 —— 41列表渲染 - 进阶用法
这一节讲解列表渲染的两个进阶用法: 如果需要对默认的变量名和下标进行修改,可以使用 wx:for-item 和 wx:for-item: 使用 wx:for-item 可以指定数组当前元素的变量名使用 wx:for-index 可以指定数组当前下标的变量名 将 wx:for 用在 标签上&…...
ElasticSearch 入门教程
ElasticSearch 入门教程 ElasticSearch 是一个分布式、可扩展的搜索和分析引擎,基于 Apache Lucene 构建,支持全文检索、结构化查询和聚合分析。本教程将带你深入了解 ElasticSearch 的核心概念、安装配置、常见操作,并提供示例代码…...
用Python写一个算24点的小程序
一、运行界面 二、显示答案——递归介绍 工作流程: 1. 基本情况:函数首先检查输入的数字列表 nums 的长度。如果列表中只剩下一个数字,它会判断这个数字是否接近 24(使用 abs(nums[0] - 24) < 1e-10 来处理浮点数精度问题&…...
分布式网络
分布式网络(Distributed Network)指的是一种计算机网络架构,其中计算资源(计算、存储、数据处理等)分布在多个物理或逻辑上的节点上,而不是集中在单一的服务器或数据中心中。这种架构的主要目标是提高系统的…...
忘记dedecms后台超级管理员账号和密码的解决方案
解决方案: 方案一、数据库修改: 1、前提是您能登录到数据库后台,登录MySQL数据库管理工具(如phpMyAdmin) 2、打开数据库中的 dede_admin 表,找到管理员记录,将 pwd 字段的值改成 f297a57a5a7…...
【Linux学习笔记】Linux基本指令分析和权限的概念
【Linux学习笔记】Linux基本指令分析和权限的概念 🔥个人主页:大白的编程日记 🔥专栏:Linux学习笔记 文章目录 【Linux学习笔记】Linux基本指令分析和权限的概念前言一. 指令的分析1.1 alias 指令1.2 grep 指令1.3 zip/unzip 指…...
Git基础之分支
常用指令 git branch 列出本地所有分支 git branch -r 列出所有远程分支 git branch [branch-name] 新建一个分支,但依然停留在当前分支 git checkout -b [branch] 新建一个分支,并切换到该分支 git merge [branch] 合并指定分支当前分支 git branch -d …...
MAC电脑常用操作
环境:M3芯片 ,macOS15.2 🚀 快捷键 🖥️ 窗口管理 ✅ 退出/进入全屏模式 • 浏览器等应用:⌘ Command Ctrl F ✅ 最小化当前窗口 • ⌘ Command M • 💡 隐藏窗口但保留应用在后台运行 ✅ 关闭当前标…...
【计算机网络】深入解析 HTTP 协议的概念、工作原理和通过 Fiddler 抓包查看 HTTP 请求/响应的协议格式
网络原理— HTTP 1. 什么是HTTP? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议: HTTP 往往是基于传输层的 TCP 协议实现的 (HTTP1.0,HTTP1.1,HTTP2.0 均为TCP,HTTP3基于UDP实现) 我们平时打开一个网站,就是通过HTTP协议来…...
Springboot redis bitMap实现用户签到以及统计,保姆级教程
项目架构,这是作为demo展示使用: Redis config: 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…...
别再让模型在Unity里‘抽风’了!Blender导出FBX到Unity的7步避坑自查清单
别再让模型在Unity里‘抽风’了!Blender导出FBX到Unity的7步避坑自查清单当你花了三天三夜精心雕琢的Blender模型,导入Unity后却变成了一团旋转错乱、贴图闪烁的"抽象艺术",那种崩溃感每个3D开发者都懂。本文将用实战经验帮你建立一…...
Tftpd32/Tftpd64不止是TFTP!手把手教你玩转它的DHCP和Syslog服务器功能
Tftpd32/Tftpd64:解锁DHCP与Syslog服务的隐藏潜力当大多数人提起Tftpd32/Tftpd64时,第一反应往往是它作为TFTP服务器的功能。这款轻量级工具确实在文件传输领域表现出色,但它的能力远不止于此。今天,我们将深入探索这款软件中两个…...
Redis沙盒体验:在浏览器中零门槛掌握NoSQL核心技能
Redis沙盒体验:在浏览器中零门槛掌握NoSQL核心技能 【免费下载链接】try.redis A demonstration of the Redis database. 项目地址: https://gitcode.com/gh_mirrors/tr/try.redis 当你第一次听说Redis时,是否被那些晦涩的技术术语吓退࿱…...
基于Arduino与应变片传感器的高精度厨房电子秤DIY全攻略
1. 项目概述:用Arduino打造一台高精度厨房电子秤作为一个喜欢在厨房里折腾的硬件爱好者,我经常遇到需要精确称量食材的场合。市面上的电子秤要么精度不够,要么价格不菲,要么功能单一。于是,我萌生了自己动手做一台的想…...
中兴光猫终极管理指南:解锁工厂模式与Telnet权限的实战教程
中兴光猫终极管理指南:解锁工厂模式与Telnet权限的实战教程 【免费下载链接】zteOnu A tool that can open ZTE onu device factory mode 项目地址: https://gitcode.com/gh_mirrors/zt/zteOnu 掌握中兴光猫的设备管理和权限获取能力是网络管理员和技术爱好者…...
基于ESP32与MQTT的家庭环境监测系统:从传感器选型到数据可视化实战
1. 项目概述与核心价值最近几年,我身边越来越多的朋友开始关注家里的空气质量、温湿度这些看不见摸不着,但又实实在在影响生活舒适度和健康的环境指标。从新装修的房子担心甲醛,到有老人小孩的家庭在意PM2.5和二氧化碳浓度,再到南…...
论文润色深度测评:GPT-5.5 + Gemini 3.1 Pro:教你学会1+1>2的论文润色方法
各位同仁好,我是七哥。一个在高校里从事人工智能相关领域研究,钻研用大模型AI实操的学术人。可以和七哥交流学术写作或Gemini、GPT、Claude等大模型学术实操相关问题,多多交流,相互成就,共同进步。 2026年的科研圈,AI工具的选择已经从有没有变成了强不强,七哥评测了GPT…...
如何快速掌握MoveIt2:面向ROS 2开发者的工业机器人运动规划完整指南
如何快速掌握MoveIt2:面向ROS 2开发者的工业机器人运动规划完整指南 【免费下载链接】moveit2 :robot: MoveIt for ROS 2 项目地址: https://gitcode.com/gh_mirrors/mo/moveit2 想要为你的机器人实现智能运动规划吗?MoveIt2作为ROS 2生态中最强大…...
艾尔登法环存档迁移终极指南:3分钟解决角色转移难题
艾尔登法环存档迁移终极指南:3分钟解决角色转移难题 【免费下载链接】EldenRingSaveCopier 项目地址: https://gitcode.com/gh_mirrors/el/EldenRingSaveCopier 还在为《艾尔登法环》存档版本不兼容而烦恼吗?EldenRingSaveCopier 是你的终极解决…...
179个核心职位,50个公司分类,中国大模型产业全栈
最后 对于正在迷茫择业、想转行提升,或是刚入门的程序员、编程小白来说,有一个问题几乎人人都在问:未来10年,什么领域的职业发展潜力最大? 答案只有一个:人工智能(尤其是大模型方向)…...
