【创建模式-单例模式(Singleton Pattern)】
赐萧瑀
- 实现方案
- 饿汉模式
- 懒汉式(非线程安全)
- 懒汉模式(线程安全)
- 双重检查锁定
- 静态内部类
- 攻击方式
- 序列化攻击
- 反射攻击
- 枚举(最佳实践)
- 枚举是一种类
唐 李世民
疾风知劲草,板荡识诚臣。
勇夫安识义,智者必怀仁。
实现单例模式的主要方式有:饿汉模式、懒汉模式(非线程安全)、懒汉模式(线程安全)、双重检查锁定、静态内部类和枚举方式。攻击方式有克隆攻击、序列化攻击和反射攻击。
实现方案
序号 | 实现方式 | 描述 | 优点 | 缺点 |
---|---|---|---|---|
1 | 饿汉式 | 在类加载时就创建实例 | 实现简单,线程安全 | 如果实例未被使用,会造成资源浪费 |
2 | 懒汉式(非线程安全) | 在第一次调用时创建实例 | 延迟加载,节省资源 | 非线程安全,多线程环境下可能创建多个实例 |
3 | 懒汉式(线程安全) | 在第一次调用时创建实例,并使用同步方法确保线程安全 | 延迟加载,线程安全 | 每次调用 getInstance 都需要同步,性能较差 |
4 | 双重检查锁定 | 在第一次调用时创建实例,并使用双重检查锁定机制确保线程安全 | 延迟加载,线程安全,且只在第一次创建实例时同步,性能较好 | 实现较复杂,需要注意 volatile 关键字的使用 |
5 | 静态内部类 | 利用静态内部类的特性,在第一次调用时创建实例 | 延迟加载,线程安全,实现简单 | 无法传递参数给单例实例 |
6 | 枚举 | 使用枚举类型实现单例 | 实现简单,线程安全,且能防止反射和序列化破坏单例 | 不能延迟加载,且不够灵活 |
饿汉模式
package com.cld.designpattern.creation.singleton.hungry;import java.io.Serializable;/*** 饿汉式* 是否 Lazy 初始化:否* <p>* 是否多线程安全:是* <p>* 实现难度:易* <p>* 描述:这种方式比较常用,但容易产生垃圾对象。* 优点:没有加锁,执行效率会提高。* 缺点:类加载时就初始化,浪费内存。* 它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。** @author 休克柏*/
public class HungrySingleton implements Serializable,Cloneable {private static final HungrySingleton INSTANCE = new HungrySingleton();private HungrySingleton() {if (INSTANCE != null) {throw new RuntimeException("单例构造器禁止反射调用!");}}public static HungrySingleton getInstance() {return INSTANCE;}@Overridepublic HungrySingleton clone() {//避免克隆攻击return getInstance();}
}
懒汉式(非线程安全)
import java.io.ObjectStreamException;
import java.io.Serializable;/*** (线程不安全)懒汉式* 是否 Lazy 初始化:是* * 是否多线程安全:否* * 实现难度:易* * 描述:这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加* 锁synchronized,所以严格意义上它并不算单例模式。* 这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。** @author 休克柏*/
public class Lazy1Singleton implements Serializable {private static Lazy1Singleton instance;private Lazy1Singleton() {}/*** 线程不安全** @return Lazy1Singleton*/public static Lazy1Singleton getInstance() {if (instance == null) {instance = new Lazy1Singleton();}return instance;}/*** 反序列化的时候,会调用该方法,从而避免反序列化对单例的破坏* @return Lazy1Singleton* @throws ObjectStreamException*/private Object readResolve() throws ObjectStreamException {return getInstance();}
}
懒汉模式(线程安全)
package org.cqcs.knowledge.designpattern.creation.singleton.lazy;import java.io.Serializable;/*** (线程安全)懒汉式* 是否 Lazy 初始化:是* <p>* 是否多线程安全:是* <p>* 实现难度:易* <p>* 描述:这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。* 优点:第一次调用才初始化,避免内存浪费。* 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。* getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。** @author 休克柏*/
public class Lazy2Singleton implements Serializable {private static Lazy2Singleton instance;private Lazy2Singleton() {}/*** 线程安全,但是synchronized锁比较重** @return Lazy2Singleton*/public static synchronized Lazy2Singleton getInstance() {if (instance == null) {instance = new Lazy2Singleton();}return instance;}
}
双重检查锁定
/*** JDK 版本:JDK1.5 起* * 是否 Lazy 初始化:是* * 是否多线程安全:是* * 实现难度:较复杂* * 描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。* getInstance() 的性能对应用程序很关键。* * Lazy2Singleton相对于Lazy1Singleton的效率问题,其实是为了解决1%几率的问题,* 而使用了一个100%出现的防护盾。* 那有一个优化的思路,就是把100%出现的防护盾,也改为1%的几率出现,使之只出现在可能会导致多个实例出现的地方。** @author 休克柏*/
public class DclSingleton implements Serializable {/*** volatile 的作用是对dclSingleton的写操作有一个内存屏障,这样,在它的赋值完成之前,就不用会调用读操作。* * volatile关键字通过提供“内存屏障”的方式来防止指令被重排序,为了实现volatile的内存语义,* 编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。* * 在java内存模型中,volatile 关键字作用可以是保证可见性或者禁止指令重排。* 这里是因为 dclSingleton = new DclSingleton() ,它并非是一个原子操作,事实上:* 在 JVM 中上述语句至少做了以下这 3 件事:* * 第一步是给 dclSingleton 分配内存空间;* * 第二步开始调用 DclSingleton 的构造函数等,来初始化 dclSingleton;* * 第三步,将 dclSingleton 对象指向分配的内存空间(执行完这步 dclSingleton 就不是 null 了)。* * 这里需要留意一下 1-2-3 的顺序,因为存在指令重排序的优化,也就是说第 2 步和第 3 步的顺序是* 不能保证的,最终的执行顺序,可能是 1-2-3,也有可能是 1-3-2。* 如果是 1-3-2,那么在第 3 步执行完以后,dclSingleton 就不是 null 了,可是这时第 2 步并没* 有执行,singleton 对象未完成初始化,它的属性的值可能不是我们所预期的值。* 假设此时线程 2 进入 getInstance 方法,由于 dclSingleton 已经不是 null 了,* * 所以会通过第一重检查并直接返回,但其实这时的 singleton 并没有完成初始化,所以使用这个实例的时候会报错。*/private volatile static DclSingleton dclSingleton;private DclSingleton() {}public static DclSingleton getDlcSingleton() {if (dclSingleton == null) {synchronized (DclSingleton.class) {if (dclSingleton == null) {//1. 给 dclSingleton 分配内存//2. 调用 dclSingleton 的构造函数来初始化成员变量,形成实例//3. 将dclSingleton对象指向分配的内存空间(执行完这步 singleton才是非 null了)//上述3步是dclSingleton = new DclSingleton()的指令执行顺序,dclSingleton = new DclSingleton();}}}return dclSingleton;}/*** 反序列化的时候,会调用该方法,从而避免反序列化对单例的破坏* @return Lazy1Singleton* @throws ObjectStreamException*/private Object readResolve() throws ObjectStreamException {return getDlcSingleton();}
}
静态内部类
package org.cqcs.knowledge.designpattern.creation.singleton.staticinnerclass;/***静态内部类的实现方式利用了 类加载机制 和 静态内部类的特性 来保证单例的线程安全和延迟加载。** 延迟加载:* 静态内部类不会在外部类加载时立即加载,而是在第一次调用 getInstance() 方法时才会加载内部类并创建实例。这种方式实现了延迟加载,避免了资源浪费。** 线程安全:* JVM 在加载类时是线程安全的,静态内部类在加载时会由 JVM 保证线程安全,因此不需要额外的同步机制。** 静态内部类的特性:* 静态内部类是独立于外部类的,只有在被引用时才会加载。* 静态内部类的静态成员变量(单例实例)在类加载时初始化,且只会初始化一次。* @author 休克柏*/
public class StaticInnerClass {private StaticInnerClass() {}private static class SingletonHolder {private static final StaticInnerClass INSTANCE = new StaticInnerClass();}public static StaticInnerClass getInstance() {return SingletonHolder.INSTANCE;}
}
攻击方式
序列化攻击
反射攻击
枚举(最佳实践)
There are three kinds of reference types: class types, array types, and interface types.
Their values are references to dynamically created class instances, arrays,
or class instances or arrays that implement interfaces, respectively.
一共有三种引用类型:class types, array types, and interface types. 其值指向动态创建的类实例,数组或者分别实现了接口的类实例或者数组。
从中我们可以得知enum不是一种专门的引用数据类型,它是类。
枚举是一种类
Day.java
public enum Day {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
}
- 编译Day.java生成Day.class
javac Day.java
- 反编译Day.class
javap -c Day.class
Compiled from "Day.java"
public final class demo.Day extends java.lang.Enum<demo.Day> {public static final demo.Day MONDAY;public static final demo.Day TUESDAY;public static final demo.Day WEDNESDAY;public static final demo.Day THURSDAY;public static final demo.Day FRIDAY;public static final demo.Day SATURDAY;public static final demo.Day SUNDAY;public static demo.Day[] values();Code:0: getstatic #1 // Field $VALUES:[Ldemo/Day;3: invokevirtual #2 // Method "[Ldemo/Day;".clone:()Ljava/lang/Object;6: checkcast #3 // class "[Ldemo/Day;"9: areturn………………
从输出我们可以发现定义语句public final class demo.Day extends java.lang.Enum<demo.Day>知道我们的Day.java就是一个类,该类继承了java.lang.Enum.
/*** This is the common base class of all Java language enumeration types.** More information about enums, including descriptions of the* implicitly declared methods synthesized by the compiler, can be* found in section 8.9 of* <cite>The Java™ Language Specification</cite>.** <p> Note that when using an enumeration type as the type of a set* or as the type of the keys in a map, specialized and efficient* {@linkplain java.util.EnumSet set} and {@linkplain* java.util.EnumMap map} implementations are available.** @param <E> The enum type subclass* @author Josh Bloch* @author Neal Gafter* @see Class#getEnumConstants()* @see java.util.EnumSet* @see java.util.EnumMap* @since 1.5*/
@SuppressWarnings("serial") // No serialVersionUID needed due to// special-casing of enum types.
public abstract class Enum<E extends Enum<E>>implements Comparable<E>, Serializable {
相关文章:
【创建模式-单例模式(Singleton Pattern)】
赐萧瑀 实现方案饿汉模式懒汉式(非线程安全)懒汉模式(线程安全)双重检查锁定静态内部类 攻击方式序列化攻击反射攻击 枚举(最佳实践)枚举是一种类 唐 李世民 疾风知劲草,板荡识诚臣。 勇夫安识义,智者必怀仁…...

攻防世界你猜猜
打开题目发现是一串十六进制的数据 我尝试解码了一下没发现什么,最后找了一下发现因为这是504B0304开头的所以是一个zip文件头 用python代码还原一下 from Crypto.Util.number import * f open("guess.zip","wb") s 0x504B03040A0001080000…...

【Axure教程】标签版分级多选下拉列表
分级多选下拉列表是指一个下拉列表,它包含多个层次的选项,用户可以选择一个或多个选项。这些选项通常是根据某种层级关系来组织的,例如从上到下有不同的分类或者过滤条件,用户选择上层选项后,下层选项会发生变化&#…...

DeepSeek图解10页PDF
以前一直在关注国内外的一些AI工具,包括文本型、图像类的一些AI实践,最近DeepSeek突然爆火,从互联网收集一些资料与大家一起分享学习。 本章节分享的文件为网上流传的DeepSeek图解10页PDF,免费附件链接给出。 1 本地 1 本地部…...

Centos7 停止维护,docker 安装
安装docker报错 执行docker安装命令:sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin,出现如下错误 更换yum源 [rootlocalhost yum.repos.d]# sudo mv /etc/yum.repos.d/CentOS-Base.repo /et…...
日志级别修改不慎引发的一场CPU灾难
背景 今天下午16.28有同事通过日志配置平台将某线上应用部分包的日志等级由error调为info,进而导致部分机器CPU升高,甚至有机器CPU达到100%,且ygc次数增加,耗时增加到80~100ms。 故障发现与排查 16.28陆续出现线上C…...

FPGA实现SDI视频缩放转UltraScale GTH光口传输,基于GS2971+Aurora 8b/10b编解码架构,提供2套工程源码和技术支持
目录 1、前言工程概述免责声明 2、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目我这里已有的 GT 高速接口解决方案本博已有的 SDI 编解码方案我这里已有的FPGA图像缩放方案 3、工程详细设计方案工程设计原理框图SDI 输入设备GS2971芯片BT1120转RGB…...

二级C语言题解:矩阵主、反对角线元素之和,二分法求方程根,处理字符串中 * 号
目录 一、程序填空📝 --- 矩阵主、反对角线元素之和 题目📃 分析🧐 二、程序修改🛠️ --- 二分法求方程根 题目📃 分析🧐 三、程序设计💻 --- 处理字符串中 * 号 题目…...
利用 Python 爬虫获取按关键字搜索淘宝商品的完整指南
在电商数据分析和市场研究中,获取商品的详细信息是至关重要的一步。淘宝作为中国最大的电商平台之一,提供了丰富的商品数据。通过 Python 爬虫技术,我们可以高效地获取按关键字搜索的淘宝商品信息。本文将详细介绍如何利用 Python 爬虫技术获…...
什么是幂等性
幂等性(Idempotence)是一个在数学、计算机科学等多个领域都有重要应用的概念,下面从不同领域为你详细介绍其含义。 数学领域 在数学中,幂等性是指一个操作或函数进行多次相同的运算,其结果始终与进行一次运算的结果相…...

群晖NAS如何通过WebDAV和内网穿透实现Joplin笔记远程同步
文章目录 前言1. 检查群晖Webdav 服务2. 本地局域网IP同步测试3. 群晖安装Cpolar工具4. 创建Webdav公网地址5. Joplin连接WebDav6. 固定Webdav公网地址7. 公网环境连接测试 前言 在数字化浪潮的推动下,笔记应用已成为我们记录生活、整理思绪的重要工具。Joplin&…...

示例:JAVA调用deepseek
近日,国产AI DeepSeek在中国、美国的科技圈受到广泛关注,甚至被认为是大模型行业的最大“黑马”。在外网,DeepSeek被不少人称为“神秘的东方力量”。1月27日,DeepSeek应用登顶苹果美国地区应用商店免费APP下载排行榜,在…...

【提示工程】:如何有效与大语言模型互动
随着人工智能技术的快速发展,大语言模型(LLM)如 GPT 系列在各类任务中的应用越来越广泛。从文本生成到代码编写,从数据分析到内容创作,这些模型展现出了强大的能力。然而,要充分发挥大语言模型的潜力,关键在于如何设计高质量的提示词(Prompts)。这门技术被称为提示工程…...

操作系统—经典同步问题
补充 互斥信号量mutex初值均为1 同步信号量根据问题实际描述自己设计 生产者-消费者问题 问题描述:一组生产者进程和一组消费者进程 共享一个初始为空、大小为n的缓冲区。(缓冲区:临界资源) 只有缓冲区没满时,生产者…...

profinet工业通信协议网关:提升钢铁冶炼智能制造效率的利器
工业通信协议网关profinet转ethercat(稳联技术WL-PN-ECATM)在钢铁冶炼生产线中的智能应用实践 在现代钢铁冶炼生产中,复杂的设备互联和数据传输对生产效率和质量控制至关重要。本案例详细阐述了某大型钢铁集团通过工业通信协议网关实现生产线…...
Vue基础:计算属性(描述依赖响应式状态的复杂逻辑)
文章目录 引言computed() 方法期望接收一个 getter 函数可写计算属性:计算属性的 Setter计算属性的缓存机制调试 Computed引言 推荐使用计算属性来描述依赖响应式状态的复杂逻辑 computed 函数:它接受 getter 函数并为 getter 返回的值返回一个不可变的响应式 ref 对象。 c…...

leetcode:1534. 统计好三元组(python3解法)
难度:简单 给你一个整数数组 arr ,以及 a、b 、c 三个整数。请你统计其中好三元组的数量。 如果三元组 (arr[i], arr[j], arr[k]) 满足下列全部条件,则认为它是一个 好三元组 。 0 < i < j < k < arr.length|arr[i] - arr[j]| &l…...

BUU27 [SUCTF 2019]CheckIn1
题目是上传文件 直接上传muma.jpg还不成功: 好吧,那做一个图片马上去,换马以后发现还是不行,呃啊啊啊啊 干啥啥不行,搜wp第一名,哎 新面孔:exif_imagetype 函数在 PHP 中用于检测一个文件是否为…...

unity学习30:Audio Source, Audio clip 音效和音乐
目录 1 音乐相关必须要有 Audio listener 和Source 2 Scene里必须要有 Audio listener 3 Audio Source 3.1 Audio Source 就是音源,可播放的音乐clip 分类 3.2 创建Audio Source 3.3 各种属性 3.4 3D sound Settings 4 使用脚本来播放声音 4.1 声明AudioC…...

【Qt 常用控件】输入类控件1(QLineEdit和QTextEdit 输入框)
目录 1.QLineEdit 单行输入框 例:输入个人信息,通过按钮提交 例:为输入框设置验证器,检查输入的电话 例:验证两次输入的密码是否一致 例:是否显示密码按钮,toggled信号。 2.QTextEdit多行输入框 、QPl…...

前端基础之《Vue(18)—路由知识点》
一、两种路由模式 1、hash路由 (1)url中有#号,背后是监听onhashchange事件 (2)hash路由部署上线不会出现404问题,背后是基于history api实现的 2、history路由 (1)url中没有#号 &a…...
关于 java:3. Java 常用类库与数据结构
一、String 1.1 String 是什么? public final class String implements java.io.Serializable, Comparable<String>, CharSequence特点: 是 不可变对象(immutable) 是 final 类,不能被继承 内部使用 字符数组…...

Windows版PostgreSQL 安装 vector 扩展
问题 spring-ai在集成PGVector向量存储的时候会报错如下,那么就需要安装pgsql的vector扩展。 SQL [CREATE EXTENSION IF NOT EXISTS vector]; 错误: 无法打开扩展控制文件 "C:/Program Files/PostgreSQL/9.6/share/extension/vector.control": No such …...

vscode使用“EIDE”和“Cortex-Debug”插件利用st-link插件实现程序烧写以及调试工作
第一步:安装vscode插件“EIDE”EIDE和“Cortex-Debug”。 第二步:配置EIDE 2.1安装“实用工具”: 2.2 EIDE插件配置:根据安装的keil C51 keil MDK IAR的相关路径设置 第三步:配置Cortex-Debug插件 点击settings.jso…...

pikachu通关教程-目录遍历漏洞(../../)
目录遍历漏洞也可以叫做信息泄露漏洞、非授权文件包含漏洞等. 原理:目录遍历漏洞的原理比较简单,就是程序在实现上没有充分过滤用户输入的../之类的目录跳转符,导致恶意用户可以通过提交目录跳转来遍历服务器上的任意文件。 这里的目录跳转符可以是../…...
2025年微信小程序开发:趋势、最佳实践与AI整合
引言 微信小程序自2017年推出以来,已成为中国互联网生态中不可或缺的一部分。根据最新数据,截至2024年,微信小程序的日活跃用户超过4.5亿,总数超过430万个,95%的中国企业拥有自己的小程序(WeChat Mini Pro…...

Agentic Workflow是什么?Agentic Workflow会成为下一个AI风口吗?
无论是想要学习人工智能当做主业营收,还是像我一样作为开发工程师但依然要运用这个颠覆开发的时代宠儿,都有必要了解、学习一下人工智能。 近期发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,入行门槛低&#x…...
Spring Cloud Eureka:微服务架构中的服务注册与发现核心组件
前言 在微服务架构日益流行的今天,服务注册与发现机制成为了构建弹性、可扩展分布式系统的关键。作为Spring Cloud生态中的核心组件,Eureka为微服务架构提供了高效的服务注册与发现解决方案。本文将深入探讨Eureka的设计原理、核心机制以及在实际项目中…...

【论文解读】ReAct:从思考脱离行动, 到行动反馈思考
认识从实践开始,经过实践得到了理论的认识,还须再回到实践去。 ——《实践论》,毛泽东 1st author: About – Shunyu Yao – 姚顺雨 paper [2210.03629] ReAct: Synergizing Reasoning and Acting in Language ModelsReAct: Synergizing Reasoning and…...
PostgreSQL不同的等级认证体系
PostgreSQL 专家认证有不同的等级和体系,以工业和信息化部人才交流中心推出的认证为例,分为 PGCA 认证专员、PGCP 认证专家、PGCM 认证大师三个等级。以下是学习建议: 明确学习目标与认证等级 PGCA初级认证专员:适合刚接触 Post…...