【创建模式-单例模式(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&trade; 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 {

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

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

相关文章

20240206 adb 连不上手机解决办法

Step 1: lsusb 确认电脑 usb 端口能识别设备 lsusb不知道设备有没有连上&#xff0c;就插拔一下&#xff0c;对比观察多了/少了哪个设备。 Step 2: 重启 adb server sudo adb kill-serversudo adb start-serveradb devices基本上就可以了&#xff5e; Reference https://b…

《利用原始数据进行深度神经网络闭环 用于光学驻留空间物体检测》论文精读

Deep Neural Network Closed-loop with Raw Data for Optical Resident Space Object Detection 摘要 光学勘测是观测驻留空间物体和空间态势感知的重要手段。随着天文技术和还原方法的应用&#xff0c;宽视场望远镜在发现和识别驻留空间物体方面做出了重大贡献。然而&#x…

2025年前端面试准备html篇

1.对html 语义化标签的理解 html语义化标签简单来说页面有良好的结构&#xff0c;使元素有含义便于理解。 优点可以使页面呈现出清晰的机构&#xff0c;有利于seo和搜索引擎抓取信息&#xff0c;便于团队的开发和管理。 常见的语义化标签有&#xff1a; <header> - 定义页…

【字节青训营-9】:初探字节微服务框架 Hertz 基础使用及进阶(下)

本文目录 一、Hertz中间件Recovery二、Hertz中间件跨资源共享三、Hertz 响应四、Hertz请求五、Hertz中间件Session 一、Hertz中间件Recovery Recovery中间件是Hertz框架预置的中间件&#xff0c;使用server.Default()可以默认注册该中间件&#xff0c;为Hertz框架提供panic回复…

因果推断与机器学习—用机器学习解决因果推断问题

Judea Pearl 将当前备受瞩目的机器学习研究戏谑地称为“仅限于曲线拟合”,然而,曲线拟合的实现绝非易事。机器学习模型在图像识别、语音识别、自然语言处理、蛋白质分子结构预测以及搜索推荐等多个领域均展现出显著的应用效果。 在因果推断任务中,在完成因果效应识别之后,需…

软件设计模式

目录 一.创建型模式 抽象工厂 Abstract Factory 构建器 Builder 工厂方法 Factory Method 原型 Prototype 单例模式 Singleton 二.结构型模式 适配器模式 Adapter 桥接模式 Bridge 组合模式 Composite 装饰者模式 Decorator 外观模式 Facade 享元模式 Flyw…

Linux 零拷贝技术

一、传统做法&#xff0c;经历“四次拷贝” 数据 1.读取到内核缓冲区 2.拷贝到用户缓冲区 3.写入到内核缓冲区 4.拷贝到网卡 使用 DMA&#xff0c;减少2次拷贝&#xff0c;还剩2次拷贝 DMA 负责硬盘到内核缓冲区和内核到网卡的传输。 CPU 仍需处理内核和用户缓冲区之间的数据…

【通俗易懂说模型】线性回归(附深度学习、机器学习发展史)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;深度学习_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2. …

利用Vue和javascript分别编写一个“Hello World”的定时更新

目录 一、利用Vue编写一个“Hello World”的定时更新&#xff08;1&#xff09;vue编码在Html文件中&#xff08;2&#xff09;vue编码在js文件中 二、利用javascript编写一个“Hello World”的定时更新 一、利用Vue编写一个“Hello World”的定时更新 &#xff08;1&#xff…

蓝桥杯python基础算法(2-1)——排序

目录 一、排序 二、例题 P3225——宝藏排序Ⅰ 三、各种排序比较 四、例题 P3226——宝藏排序Ⅱ 一、排序 &#xff08;一&#xff09;冒泡排序 基本思想&#xff1a;比较相邻的元素&#xff0c;如果顺序错误就把它们交换过来。 &#xff08;二&#xff09;选择排序 基本思想…

洛谷 P4552 [Poetize6] IncDec Sequence C语言

P4552 [Poetize6] IncDec Sequence - 洛谷 | 计算机科学教育新生态 题目描述 给定一个长度为 n 的数列 a1​,a2​,…,an​&#xff0c;每次可以选择一个区间 [l,r]&#xff0c;使这个区间内的数都加 1 或者都减 1。 请问至少需要多少次操作才能使数列中的所有数都一样&#…

Vue Dom截图插件,截图转Base64 html2canvas

安装插件 npm install html2canvas --save插件使用 <template><div style"padding: 10px;"><div ref"imageTofile" class"box">发生什么事了</div><button click"toImage" style"margin: 10px;&quo…

88.[4]攻防世界 web php_rce

之前做过&#xff0c;回顾&#xff08;看了眼之前的wp,跟没做过一样&#xff09; 属于远程命令执行漏洞 在 PHP 里&#xff0c;system()、exec()、shell_exec()、反引号&#xff08;&#xff09;等都可用于执行系统命令。 直接访问index.php没效果 index.php?sindex/think\a…

数据结构-堆和PriorityQueue

1.堆&#xff08;Heap&#xff09; 1.1堆的概念 堆是一种非常重要的数据结构&#xff0c;通常被实现为一种特殊的完全二叉树 如果有一个关键码的集合K{k0,k1,k2,...,kn-1}&#xff0c;把它所有的元素按照完全二叉树的顺序存储在一个一维数组中&#xff0c;如果满足ki<k2i…

oracle 基础语法复习记录

Oracle SQL基础 因工作需要sql能力&#xff0c;需要重新把sql这块知识重新盘活&#xff0c;特此记录学习过程。 希望有新的发现。加油&#xff01;20250205 学习范围 学习SQL基础语法 掌握SELECT、INSERT、UPDATE、DELETE等基本操作。 熟悉WHERE、GROUP BY、ORDER BY、HAVIN…

【Rust自学】20.2. 最后的项目:多线程Web服务器

说句题外话&#xff0c;这篇文章非常要求Rust的各方面知识&#xff0c;最好看一下我的【Rust自学】专栏的所有内容。这篇文章也是整个专栏最长&#xff08;4762字&#xff09;的文章&#xff0c;需要多次阅读消化&#xff0c;最好点个收藏&#xff0c;免得刷不到了。 喜欢的话…

国产编辑器EverEdit - 工具栏说明

1 工具栏 1.1 应用场景 当用户想显示/隐藏界面的标签栏、工具栏、状态栏、主菜单等界面元素时&#xff0c;可以通过EverEdit的菜单选项进行设置。 1.2 使用方法 选择菜单查看 -> 工具栏&#xff0c;在工具栏的子菜单中选择勾选或去掉勾选对应的选项。 标签栏&#xff1…

虚幻UE5手机安卓Android Studio开发设置2025

一、下载Android Studio历史版本 步骤1&#xff1a;虚幻4.27、5.0、5.1、5.2官方要求Andrd Studio 4.0版本&#xff1b; 5.3、5.4、5.5官方要求的版本为Android Studio Flamingo | 2022.2.1 Patch 2 May 24, 2023 虚幻官网查看对应Andrd Studiob下载版本&#xff1a; https:/…

VLAN 基础 | 不同 VLAN 间通信实验

注&#xff1a;本文为 “ Vlan 间通信” 相关文章合辑。 英文引文&#xff0c;机翻未校。 图片清晰度限于原文图源状态。 未整理去重。 How to Establish Communications between VLANs? 如何在 VLAN 之间建立通信&#xff1f; Posted on November 20, 2015 by RouterSwi…

bat脚本实现自动化漏洞挖掘

bat脚本 BAT脚本是一种批处理文件&#xff0c;可以在Windows操作系统中自动执行一系列命令。它们可以简化许多日常任务&#xff0c;如文件操作、系统配置等。 bat脚本执行命令 echo off#下面写要执行的命令 httpx 自动存活探测 echo off httpx.exe -l url.txt -o 0.txt nuc…