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

Java:单例模式(Singleton Pattern)及实现方式

一、单例模式的概念

单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点来访问该实例,是 Java 中最简单的设计模式之一。该模式常用于需要全局唯一实例的场景,例如日志记录器、配置管理、线程池、数据库连接池等。

注意:单例模式的构造方法必须要声明为private类型。

欢迎关注工 众号:ItBeeCoder,查看更多高质量技术文章,发送“后端”获取资料

二、实现方式

在Java中,单例模式有多种实现方式,每种方式都有其适用场景和优缺点。下面主要从每种方式的特点、Java代码实现方式及优缺点三个方面介绍几种常见的实现方式。

1.饿汉式(Eager Initialization)

1)特点

类加载时立即创建并初始化实例,是线程安全的,但由于有些类的实例并不会立即用到,这种方式可能会浪费资源,尤其是创建占用资源较多的大对象时,尤为明显。

2)代码实现

public class EagerSingleton {// 类加载时直接创建实例private static final EagerSingleton instance = new EagerSingleton();// 私有构造函数防止外部实例化,这里构造函数必须为私有的private EagerSingleton() {}public static EagerSingleton getInstance() {return instance;}
}

3)优缺点
使用这种方式创建单个示例的优缺点都比较明显。
优点:简单、线程安全。
缺点:实例在类加载时创建,若从未使用会导致资源浪费。

对于计算机技术感兴趣的读者,可以加入下裙,和更多行业爱好者分享、交流最新的技术。

在这里插入图片描述

2. 同步方法懒汉式(Lazy)(线程安全,性能差)

1)特点
这种方法通过在实例创建方法getInstance()前加synchronized关键字保证多线程环境下只能创建一个实例对象,这种方法保证线程安全,但性能较低(使用了synchronized 关键字,会导致操作系统用户态和内核态的切换,耗费时间和性能,深层原因后面会在介绍synchronized 关键字的文章里详细介绍)。

2)代码实现

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

3)优缺点
优点:这种方法是线程安全的,synchronized关键字保证多线程环境下只能创建一个实例对象。
缺点:每次调用getInstance()都需要同步,性能差。

欢迎关注工 众号:ItBeeCoder,查看更多高质量技术文章,发送“后端”获取资料

3. 懒汉式(Lazy Initialization,不推荐,了解即可)

1)特点

该种实现方式延迟初始化实例对象,存在多线程安全问题。

2)代码实现

public class LazySingleton {private static LazySingleton instance;private LazySingleton() {}public static LazySingleton getInstance() {Aif (instance == null) {B)      instance = new LazySingleton();}return instance;}
}

3)优缺点
优点:延迟加载,节省资源。
缺点:这里instance实例前未加volatile关键字,并且getInstance()方法未加同步关键字或者加锁,这种方法是非线程安全的,多线程环境下可能创建多个实例。

例如:有两个线程a和b同时要创建LazySingleton 类的实例对象,a、b线程执行到 A) 处时,满足if中的判断条件,就会创建一个LazySingleton 类的实例对象。b线程这时也满足if条件,同样会创建一个实例,所以这种方法并不能保证多线程环境下只创建一个对象。

笔者注:这种方式建议了解即可,真正项目中不要用这种方式。

4. 双重检查锁定(Double-Checked Locking,DCL,推荐该种方法)

1)特点
减少同步次数,兼顾性能与线程安全。
欢迎关注工 众号:ItBeeCoder,查看更多高质量技术文章,发送“后端”获取资料

2)代码实现

public class DCLSingleton {// 这里使用volatile的作用是禁止指令重排序,因为JAVA中创建对象包括几步:如果不使用volatile,private static volatile DCLSingleton instance;private DCLSingleton() {}public static DCLSingleton getInstance() {Aif (instance == null) { // 第一次检查Bsynchronized (DCLSingleton.class) {Cif (instance == null) { // 第二次检查D)             instance = new DCLSingleton();}}}return instance;}
}

3)优缺点
优点:这种方法是线程安全的,并且在真正用到对象的时候,才会进行对象的创建(懒汉式,延迟创建),避免了提前创建对象导致的资源浪费,性能较好。
缺点:volatile关键在JDK 1.5及以后的版本才支持,JDK1.5以前的版本并不支持该种写法,需要注意,另外代码实现方式稍微复杂一些。

4)该方法使用Volatile关键字的原因
假设当前有两个线程1和2都要获取DCLSingleton 类的实例,线程1执行到A)处,满足条件进入到if判断中,接着进入到同步代码块,执行到B)行代码后,线程1的CPU执行时间片用完,让出CPU,线程2开始执行,线程2满足A)、C)处的判断条件,一直执行到D)处,并开始创建对象。Java中对象的创建主要分为几个步骤:1)声明一个引用变量(值为null);2)创建对象并为对象分配内存空间;3)初始化对象4)将对象的内存地址赋给引用变量。JVM在执行代码指令时,会根据情况对指令进行重排序。如果JVM将创建对象的指令重排序为2)、3)、1)、4),线程2执行完2)和3)后时间片用完,这些对象的引用仍为NULL。线程1)接着执行,会重新创建一个对象。最终导致多线程环境下创建多个实例。

volatile关键字可以防止指令重排序,所以这里在instance实例声明前加了该关键字,创建对象的时候会完全按照1)、2)、3)、4)的步骤执行,避免多线程环境下线程不安全的问题。

5. 静态内部类(Holder模式)

1)特点
利用类加载机制保证线程安全,延迟加载且无需同步。

2)代码实现

public class HolderSingleton {private HolderSingleton() {}// 静态内部类持有实例private static class Holder {private static final HolderSingleton INSTANCE = new HolderSingleton();}public static HolderSingleton getInstance() {return Holder.INSTANCE;}
}

3)优缺点
优点:线程安全、延迟加载、无同步开销。
缺点:无法传递参数初始化实例。

6. 枚举(Enum)

1)特点
枚举是最简洁、安全的单例实现方式,天然防反射和序列化破坏。

2)代码实现

public enum EnumSingleton {INSTANCE; // 单例实例// 添加其它业务方法public void doSomething() {System.out.println("Singleton method");
// 其它业务代码}
}

3)优缺点
优点:线程安全、防止反射攻击、自动处理序列化。
缺点:无法继承其他类(枚举类已继承Enum)。

Java中单例模式有多种实现方式,包括饿汉式、懒汉式、同步方法懒汉式、双重检查锁定、静态内部类和枚举。每种方式都有其优缺点和适用场景:饿汉式简单但可能浪费资源;懒汉式延迟加载但非线程安全;同步方法懒汉式线程安全但性能低;双重检查锁定和静态内部类都提供了线程安全和延迟加载的解决方案;枚举是最简洁安全的单例实现,防止反射和序列化破坏。文章总结了对不同场景的选择建议,并提醒谨慎使用单例模式,以防代码耦合度高和难以测试。

笔者在项目开发过程中一直用的是本文介绍的方法双重检查锁定。这种方法属于延迟加载,能避免提前创建实例导致的资源浪费。

单例模式的核心要点有3方面:
1)唯一性:保证一个类在系统中只有一个实例。
2)控制访问:提供一个全局访问点来获取该实例。
3)延迟初始化(可选):实例化延迟到第一次使用时。

使用建议:
对于需要延迟加载且高并发的场景,建议优先选择静态内部类或双重检查锁定。在需要防反射或序列化攻击的场景,则必须使用枚举。



又到了金三银四求职季,我整理了一些互联网大厂的面试题,有需要的可关注工 众号:ItBeeCoder,发送“后端”获取
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

相关文章:

Java:单例模式(Singleton Pattern)及实现方式

一、单例模式的概念 单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点来访问该实例,是 Java 中最简单的设计模式之一。该模式常用于需要全局唯一实例的场景,例如日志记录器、配置管理、线程池、数据库…...

Python爬虫实战:股票分时数据抓取与存储 (1)

在金融数据分析中,股票分时数据是投资者和分析师的重要资源。它能够帮助我们了解股票在交易日内的价格波动情况,从而为交易决策提供依据。然而,获取这些数据往往需要借助专业的金融数据平台,其成本较高。幸运的是,通过…...

将图片base64编码后,数据转成图片

将图片数据进行base64编码后,可以在浏览器上查看图片,只需在前端加上data:image/png;base64,即可 在线工具: Base64转图片 - 加菲工具...

天翼云910B部署DeepSeek蒸馏70B LLaMA模型实践总结

一、项目背景与目标 本文记录在天翼云昇腾910B服务器上部署DeepSeek 70B模型的全过程。该模型是基于LLaMA架构的知识蒸馏版本,模型大小约132GB。 1.1 硬件环境 - 服务器配置:天翼云910B服务器 - NPU:8昇腾910B (每卡64GB显存) - 系统内存&…...

Budibase低代码平台体验

低代码平台还是很多的,体验了Nocobase,又开始体验Budibase, 其实Budibase和appsmith更相似一点。 Budibase的安装也很简单。 1.安装好操作系统Debian; 2.安装好docker, docker-compose 3.创建目录/data,在里面参考内容创建文件docker-compos…...

【R语言】GitHub Copilot安装-待解决

参考: 文章目录...

Playwright 自动化测试系统学习

入门 Playwright安装:Playwright入门之---安装-CSDN博客 生成测试:Playwright入门之---生成测试-CSDN博客 命令汇总:Playwright入门之---命令-CSDN博客...

Jetson Agx Orin平台preferred_stride调试记录--1924x720图像异常

1.问题描述 硬件: AGX Orin 在Jetpack 5.0.1和Jetpack 5.0.2上测试验证 图像分辨率在1920x720和1024x1920下图像采集正常 但是当采集图像分辨率为1924x720视频时,图像输出异常 像素格式:yuv_uyvy16 gstreamer命令如下 gst-launch-1.0 v4l2src device=/dev/video0 ! …...

DeepSeek冲击(含本地化部署实践)

DeepSeek无疑是春节档最火爆的话题,上线不足一月,其全球累计下载量已达4000万,反超ChatGPT成为全球增长最快的AI应用,并且完全开源。那么究竟DeepSeek有什么魔力,能够让大家趋之若鹜,他又将怎样改变世界AI格…...

CF 144A.Arrival of the General(Java实现)

题目分析 一个n个身高数据,问最高的到最前面,最矮的到最后面的最短交换次数 思路分析 首先,如果数据有重复项,例如示例二中,最矮的数据就是最后一个出现的数据位置,最高的数据就是最先出现的数据位置&…...

set的使用(c++)

STL里面已经为我们实现了两种红黑树,一种是存储关键字的set,另一种是存储双关键字的map,今天主要来了解set,无论是set还是map后面都跟一个multi,它们区别是set 不能存相同元素, multiset 可以存相同的元素&…...

未加cont修饰的左值引用不能绑定到右值

目录 一、问题背景 二、错误分析 三、警告分析 一、问题背景 在initial value of reference to non-const - C Forum看到如下有问题的代码&#xff0c;编译如下代码看看 #include <iostream> #include <cmath>int g(double x) { return std::floor(x); } int&a…...

5.日常英语笔记

sprouted tater 发芽的土豆 fluid 液体&#xff0c;流体 The doctor recommended drinking plenty of fluids 医生建议多喝流质 适应新环境 adapt to the new environment adjust to the new surroundings get used to the new setting accommodate oneself to the new circu…...

IDEA单元测试插件 SquareTest 延长试用期权限

SquareTest是一款强大的IDEA单元测试生成插件工具&#xff0c;具体使用方法就不过多介绍了&#xff0c;这里主要介绍变更试用期&#xff0c;方便大家使用 配置信息 我的电脑安装前提配置条件 IntelliJ IDEA 2023.2windows 系统 软件安装 IntelliJ IDEA 直接安装插件Squar…...

25/2/17 <嵌入式笔记> 桌宠代码解析

这个寒假跟着做了一个开源的桌宠&#xff0c;我们来解析下代码&#xff0c;加深理解。 代码中有开源作者的名字。可以去B站搜着跟着做。 首先看下main代码 #include "stm32f10x.h" // Device header #include "Delay.h" #include &quo…...

C/C++字符串格式化全解析:从printf到std::format的安全演进与实战指南

目录 C 语言中的格式化函数对比 1. printf / fprintf / sprintf 的异同 C 中的字符串格式化 1. 流式输出 (std::ostringstream) 2. C20/23 格式化库 (std::format&#xff0c;需编译器支持) 跨语言对比与最佳实践 实战建议 总结 C 语言中的格式化函数对比 1. printf / …...

油田安全系统:守护能源生命线的坚固壁垒

油田安全系统&#xff1a;不可或缺的能源护盾 在能源领域&#xff0c;油田作为国家重要的能源供应基地&#xff0c;其安全生产的重要性不言而喻。油田安全系统犹如一道坚固的护盾&#xff0c;全方位守护着人员生命、企业财产以及生态环境&#xff0c;是油田平稳运行与可持续发展…...

【Python】实现文件移动与文件夹删除工具

【Python】 实现文件移动与文件夹删除工具 一、代码整体结构界面创建选择文件夹移动并删除操作处理文件重名问题打开文件夹 二、功能介绍三、 作者有话说 在日常的文件管理工作中&#xff0c;我们常常需要将某个文件夹下子文件夹中的文件统一移动到主文件夹&#xff0c;并删除这…...

LeetCode-680. 验证回文串 II

1、题目描述&#xff1a; 给你一个字符串 s&#xff0c;最多 可以从中删除一个字符。 请你判断 s 是否能成为回文字符串&#xff1a;如果能&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;s "aba" 输出&a…...

【故障处理】- 执行命令crsctl query crs xxx一直hang

【故障处理】- 执行命令crsctl query crs xxx一直hang 一、概述二、故障处理三、解决方法 一、概述 Oracle RAC环境中&#xff0c;遇到执行crsctl query crs xxx等相关命令不返回任何结果&#xff0c;一直hang在那里。系统下执行命令ps -ef |grep crsctl query crs softwarever…...

JMeter工具介绍、元件和组件的介绍

Jmeter功能概要 JDK常用文件目录介绍 Bin目录&#xff1a;存放可执行文件和配置文件 Docs目录&#xff1a;是Jmeter的API文档&#xff0c;用于开发扩展组件 printable_docs目录&#xff1a;用户帮助手册 lib目录&#xff1a;存放JMeter依赖的jar包和用户扩展所依赖的Jar包…...

【Python 语法】Python 正则表达式(regular expressions, regex)

1. 基本语法1.1 字符匹配1.2 元字符1.3 特殊字符1.4 分组和捕获1.5 断言2. 常用函数2.1 `re.match()`2.2 `re.search()`2.3 `re.findall()`2.4 `re.sub()`2.5 `re.split()`3. 进阶用法3.1 捕获组3.2 非捕获组3.3 预查Python 中的**正则表达式(regular expressions, regex)**是…...

在 Python 里,None 可能是调用者主动传入的值,所以不能用 None 来判断参数是否被提供。

在 Python 里&#xff0c;None 可能是调用者主动传入的值&#xff0c;所以不能用 None 来判断参数是否被提供。 使用 object() 生成一个特殊的 唯一标记变量&#xff0c;用作默认参数的占位符&#xff0c;就可以明确区分调用者是否真的传递了这个参数。 &#x1f4cc; 为什么 …...

DeepSeek 引领AI 大模型时代,服务器产业如何破局进化?

2025 年 1 月&#xff0c;DeepSeek - R1 以逼近 OpenAI o1 的性能表现&#xff0c;在业界引起轰动。其采用的混合专家架构&#xff08;MoE&#xff09;与 FP8 低精度训练技术&#xff0c;将单次训练成本大幅压缩至 557 万美元&#xff0c;比行业平均水平降低 80%。这一成果不仅…...

安卓burp抓包,bypass ssl pinning

好久好久没有发东西了。主要是懒。。。 这几天在搞apk渗透&#xff0c;遇到了burp无法抓包问题&#xff0c;觉得可以写下来。 问题描述 1. 一台安卓手机&#xff0c;装了面具&#xff0c;可以拿到root 2. 电脑上有burp&#xff0c;设置代理 3.手机和电脑连同一个网段&…...

服务器中部署大模型DeepSeek-R1 | 本地部署DeepSeek-R1大模型 | deepseek-r1部署详细教程

0. 部署前的准备 首先我们需要足够算力的机器&#xff0c;这里我在vultr中租了有一张A16显卡一共16GB显存的服务器作为演示。部署的模型参数为14b的。如果需要部署满血版本671b的&#xff0c;需要更大的算力支持&#xff0c;这里由于是个人资金有限&#xff0c;就演示14b的部署…...

rust学习笔记2-rust的包管理工具Cargo使用

首先先解决一个配置文件&#xff0c;目前rust版本升级后&#xff0c;config已经改成 config.toml 内容也做了如下调整 [source.crates-io] replace-with tuna[source.tuna] registry "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git" 1.Rust 编程…...

DDD - 可能会用到的分布式事务

一、分布式事务的概念&#xff1a; 分布式事务是指跨越多个独立的资源或服务&#xff08;例如多个数据库、微服务、消息队列等&#xff09;执行的事务操作&#xff0c;其目标是确保整个事务在多个系统中保持原子性和一致性&#xff0c;即要么所有操作全部成功提交&#xff0c;…...

DeepSeek + Vue实战开发

利用DeepSeek V3模型、siliconflow大模型一站式云服务平台以及vue3.0实现一个在线人工智能客服对话系统。 因为deepseek官网的api密钥使用起来比较缓慢&#xff0c;所以可以使用第三方的&#xff0c;具体操作请自行查阅资料。 siliconflow官网 SiliconFlow, Accelerate AGI …...

【数据结构】(8) 二叉树

一、树形结构 1、什么是树形结构 根节点没有前驱&#xff0c;其它节点只有一个前驱&#xff08;双亲/父结点&#xff09;。所有节点可以有 0 ~ 多个后继&#xff0c;即分支&#xff08;孩子结点&#xff09;。每个结点作为子树的根节点&#xff0c;这些子树互不相交。 2、关于…...