Singleton单例设计模式详解

news2025/6/8 18:24:46

目录

  • 模式定义
  • 应用场景
  • 实现方式
    • 1.懒汉模式:
    • 2.饿汉模式:
    • 3.静态内部类
    • 反射
      • 如何防止反射攻击破坏?
    • 枚举类型
    • 序列化
  • 部分源码中的应用定位
    • Spring & JDK
    • Tomcat
    • 反序列化指定数据源

在这里插入图片描述

模式定义

保证一个类只有一个实例,并且提供一个全局访问点

应用场景

重量级的对象,不需要多个实例,如线程池,数据库连接池

实现方式

1.懒汉模式:

延迟加载,只有在真正使用的时候,才开始实例化。
1)线程安全问题。
2)double check 加锁优化。
3)编译器(JIT),CPU 有可能对指令进行重排序,导致使用到尚未初始化的实例,可以通过添加volatile 关键字进行修饰,对于volatile 修饰的字段,可以防止指令重排。

public class LazySingletonTest {
    public static void main(String[] args) {
//        LazySingleton instance1 = LazySingleton.getInstance();
//        LazySingleton instance2 = LazySingleton.getInstance();
//        System.out.println(instance1==instance2); //true
        new Thread(() -> {
            LazySingleton instance1 = LazySingleton.getInstance();
            System.out.println(instance1);
        }).start();

        new Thread(() -> {
            LazySingleton instance2 = LazySingleton.getInstance();
            System.out.println(instance2);
        }).start();

    }
}

class LazySingleton {
    private volatile static LazySingleton instance;
    private LazySingleton() {
    }
    public static LazySingleton getInstance() {
        if (instance == null) {
            synchronized (LazySingleton.class){
                if (instance == null) {
                    instance = new LazySingleton();
                    // 字节码层
                    // JIT , CPU 有可能对如下指令进行重排序
                    // 1. 分配空间 2. 初始化 3. 引用赋值
                }
            }
        }
        return instance;
    }
}

2.饿汉模式:

类加载的初始化阶段就完成了实例的初始化 。
本质上就是借助于jvm类加载机制,保证实例的唯一性(初始化过程只会执行一次)及线程安全(JVM以同步的形式来完成类加载的整个过程)。
类加载过程:

  1. 加载二进制数据到内存中, 生成对应的Class数据结构,
  2. 连接: a. 验证, b.准备(给类的静态成员变量赋默认值),c.解析
  3. 初始化: 给类的静态变量赋初值
    只有在真正使用对应的类时,才会触发初始化 如( 当前类是启动类即main函数所在类,直接进行new 操作,访问静态属性、访问静态方法,用反射访问类,初始化一个类的子类等.)
public class HungrySingletonTest {
    public static void main(String[] args) {
        HungrySingleton instance = HungrySingleton.getInstance();
        HungrySingleton instance1 = HungrySingleton.getInstance();
        System.out.println(instance==instance1);
    }
}
class HungrySingleton {
    private static HungrySingleton instance=new HungrySingleton();
    private HungrySingleton(){
    }
    public static HungrySingleton getInstance(){
        return instance;
    }
}

3.静态内部类

1).本质上是利用类的加载机制来保证线程安全
2).只有在实际使用的时候,才会触发类的初始化,所以也是懒加载的一种形式。

public class InnerClassSingletonTest {
    public static void main(String[] args) {
//        InnerClassSingleton instance = InnerClassSingleton.getInstance();
//        InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
//        System.out.println(instance==instance1); //true

        new Thread(() -> {
            InnerClassSingleton instance = InnerClassSingleton.getInstance();
            System.out.println(instance);
        }).start();

        new Thread(() -> {
            InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
            System.out.println(instance1);
        }).start();
    }
}
class InnerClassSingleton{
    private static class InnerClassHolder{
        private static InnerClassSingleton instance = new InnerClassSingleton();
    }
    private InnerClassSingleton(){
    }
    public static InnerClassSingleton getInstance(){
        return InnerClassHolder.instance;
    }
}

反射

public class InnerClassSingletonTest {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//        InnerClassSingleton instance = InnerClassSingleton.getInstance();
//        InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
//        System.out.println(instance==instance1); //true

//        new Thread(() -> {
//            InnerClassSingleton instance = InnerClassSingleton.getInstance();
//            System.out.println(instance);
//        }).start();
//
//        new Thread(() -> {
//            InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
//            System.out.println(instance1);
//        }).start();

        Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        InnerClassSingleton innerClassSingleton = declaredConstructor.newInstance();
        InnerClassSingleton instance = InnerClassSingleton.getInstance();
        System.out.println(innerClassSingleton==instance); //false
    }
}
class InnerClassSingleton{
    private static class InnerClassHolder{
        private static InnerClassSingleton instance = new InnerClassSingleton();
    }
    private InnerClassSingleton(){
    }
    public static InnerClassSingleton getInstance(){
        return InnerClassHolder.instance;
    }
}

如何防止反射攻击破坏?

饿汉模式下无法避免,懒汉模式可在构造函数中增加如下代码,判断实例是否存在。

    private InnerClassSingleton(){
        if (InnerClassHolder.instance!=null){
            throw new RuntimeException("单例不允许多个实例");
        }
    }
public class InnerClassSingletonTest {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//        InnerClassSingleton instance = InnerClassSingleton.getInstance();
//        InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
//        System.out.println(instance==instance1); //true

//        new Thread(() -> {
//            InnerClassSingleton instance = InnerClassSingleton.getInstance();
//            System.out.println(instance);
//        }).start();
//
//        new Thread(() -> {
//            InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
//            System.out.println(instance1);
//        }).start();

        Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        InnerClassSingleton innerClassSingleton = declaredConstructor.newInstance();
        InnerClassSingleton instance = InnerClassSingleton.getInstance();
        System.out.println(innerClassSingleton==instance); //false
    }
}
class InnerClassSingleton{
    private static class InnerClassHolder{
        private static InnerClassSingleton instance = new InnerClassSingleton();
    }
    private InnerClassSingleton(){
        if (InnerClassHolder.instance!=null){
            throw new RuntimeException("单例不允许多个实例");
        }
    }
    public static InnerClassSingleton getInstance(){
        return InnerClassHolder.instance;
    }
}

枚举类型

1)天然不支持反射创建对应的实例,且有自己的反序列化机制
2)利用类加载机制保证线程安全

public enum EnumSingletonTest {
    INSTANCE;

    private void print(){
        System.out.println(this.hashCode());
    }
}

class EnumTest{
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
        EnumSingletonTest instance = EnumSingletonTest.INSTANCE;
//        EnumSingletonTest instance1 = EnumSingletonTest.INSTANCE;
//        System.out.println(instance==instance1); // true

//        Constructor<EnumSingletonTest> declaredConstructor = EnumSingletonTest.class.getDeclaredConstructor(String.class,int.class);
//        declaredConstructor.setAccessible(true);
//        EnumSingletonTest instance = declaredConstructor.newInstance("INSTANCE",0);

//        ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("enumsingleton"));
//        oss.writeObject(instance);
//        oss.close();

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("enumsingleton"));
        EnumSingletonTest object = ((EnumSingletonTest) ois.readObject());
        System.out.println(instance==object); //true:有自己的反序列化机制
    }
}

序列化

可以利用指定方法readResolve来替换从反序列化流中的数据 如下

ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
ANYACCESSMODIFIER Object readResolve() throws ObjectStreamException;
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
public class InnerClassSingletonTest {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
//        InnerClassSingleton instance = InnerClassSingleton.getInstance();
//        InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
//        System.out.println(instance==instance1); //true

//        new Thread(() -> {
//            InnerClassSingleton instance = InnerClassSingleton.getInstance();
//            System.out.println(instance);
//        }).start();
//
//        new Thread(() -> {
//            InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
//            System.out.println(instance1);
//        }).start();

//        Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
//        declaredConstructor.setAccessible(true);
//        InnerClassSingleton innerClassSingleton = declaredConstructor.newInstance();
//        InnerClassSingleton instance = InnerClassSingleton.getInstance();
//        System.out.println(innerClassSingleton==instance); //false

        InnerClassSingleton instance = InnerClassSingleton.getInstance();
//        ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("testSerializable"));
//        oss.writeObject(instance);
//        oss.close();

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("testSerializable"));
        InnerClassSingleton object = ((InnerClassSingleton) ois.readObject());
        System.out.println(instance==object);

    }
}
class InnerClassSingleton implements Serializable {

    static final long serialVersionUID =42L;

    private static class InnerClassHolder{
        private static InnerClassSingleton instance = new InnerClassSingleton();
    }
    private InnerClassSingleton(){
        if (InnerClassHolder.instance!=null){
            throw new RuntimeException("单例不允许多个实例");
        }
    }
    public static InnerClassSingleton getInstance(){
        return InnerClassHolder.instance;
    }

    Object readResolve() throws ObjectStreamException{
        return InnerClassHolder.instance;
    }
}

部分源码中的应用定位

感兴趣的可以去官网下载源码深入研究

Spring & JDK

java.lang.Runtime
org.springframework.aop.framework.ProxyFactoryBean
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
org.springframework.core.ReactiveAdapterRegistry

Tomcat

org.apache.catalina.webresources.TomcatURLStreamHandlerFactory

反序列化指定数据源

java.util.Currency

创作不易,点个关注吧!!!
创作不易,点个关注吧!!!
创作不易,点个关注吧!!!
请添加图片描述

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

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

相关文章

jmeter使用之生成html测试报告

测试的最终结果是需要给出一份报告&#xff0c;那么在用jmeter测试时怎么生成一份报告呢&#xff0c;以下针对jmeter如何生成html报告进行简单介绍 一、首先把测试脚本写好二、利用命令生成html报告 命令&#xff1a;jmeter -n -t 【Jmx脚本位置】-l 【结果文件result.jtl存放…

DBSCAN简介

1. 简介 常用聚类方法有&#xff1a; 1. 层次的方法(hierarchical method)&#xff1b; 2. 划分方法(partitioning method)&#xff1b; 3. 基于密度的方法(density-based method)&#xff1b; 4. 基于网格的方法(grid-based method)&#xff1b; 5. 基于模型的方法(model-base…

[MAUI]集成富文本编辑器Editor.js至.NET MAUI Blazor项目

文章目录 获取资源从源码构建从CDN获取获取扩展插件 创建项目创建控件创建Blazor组件初始化保存销毁编写渲染逻辑 实现只读/编辑功能切换模式获取只读模式状态响应切换事件 实现明/暗主题切换项目地址 Editor.js 是一个基于 Web 的所见即所得富文本编辑器&#xff0c;它由CodeX…

【opencv】示例-npr_demo.cpp 非真实感渲染:边缘保留平滑、细节增强、铅笔素描/彩色铅笔绘图和风格化处理...

Edge Preserve Smoothing- Using Normalized convolution Filter Edge Preserve Smoothing-Using Recursive Filter Detail Enhancement Pencil sketch/Color Pencil Drawing Stylization /* * npr_demo.cpp * * 作者: * Siddharth Kherada <siddharthkherada27[at]gmail[do…

最优算法100例之46-单链表逆置-头插法

专栏主页:计算机专业基础知识总结(适用于期末复习考研刷题求职面试)系列文章https://blog.csdn.net/seeker1994/category_12585732.html 题目描述 单链表逆置-头插法 题解报告 1)带头结点 带头结点的反转链表,头结点一直不动,后移head->next。不带头结点的反转,后移…

【vue】defineEmits 传值 子传父

先行知识 【vue】导入组件【vue】defineProps 传数据 父传子 传值流程 App.vue <template><Header getWeb"emitsGetWeb" userAdd"emitsUserAdd"/><hr /><p>web.name: {{ web.name }}</p><p>web.url: {{ web.url }}&…

HarmonyOS实战开发-横竖屏切换

介绍 本实例展示如何使用媒体查询&#xff0c;通过ohos.mediaquery 接口完成在不同设备上显示不同的界面效果。 效果预览 使用说明 1.在竖屏设备上&#xff0c;首页展示新闻列表&#xff0c;点击新闻进入详情界面。 2.在横屏设备上&#xff0c;首页左侧展示新闻列表&#x…

MATLAB 自定义实现点云法向量和曲率计算(详细解读)(64)

MATLAB 自定义实现点云法向量和曲率计算(详细解读)(64) 一、算法介绍二、算法步骤三、算法实现1.代码 (完整,注释清晰,可直接用)2.结果一、算法介绍 首先说明: ------这里代码手动实现,不调用matlab提供的法向量计算接口,更有助于大家了解法向量和曲率的计算方法,…

【数据结构】复习题(二)

Hello&#xff01;大家好&#xff0c;这一篇数据结构复习题是我上个学期复习的时候写的&#xff08;刚刚在草稿箱发现了&#xff01;&#xff09;有一些题目过程都是配了图片的&#xff0c;希望对正在复习数据结构的宝宝们有帮助哦&#xff01;(还有一个数据结构复习题(一)可以…

从零开始写一个RTSP服务器(一)RTSP协议讲解

目录 前言一、什么是RTSP协议&#xff1f;二、RTSP协议详解2.1 RTSP数据格式2.2 RTSP请求的常用方法2.3 RTSP交互过程2.4 sdp格式 三、RTP协议3.1 RTP包格式3.2 RTP OVER TCP 四、RTCP 前言 为什么要写这个系列&#xff1f; 因为我自己在学习rtsp协议想自己从零写一个rtsp服务…

pytorch车牌识别

目录 使用pytorch库中CNN模型进行图像识别收集数据集定义CNN模型卷积层池化层全连接层 CNN模型代码使用模型 使用pytorch库中CNN模型进行图像识别 收集数据集 可以去找开源的数据集或者自己手做一个 最终整合成 类别分类的图片文件 定义CNN模型 卷积层 功能&#xff1a;提…

R:普通分组柱状图

输入文件实例&#xff08;存为csv格式&#xff09; library(ggplot2) library(ggbreak)# 从CSV文件中读取数据 setwd("C:/Users/fordata/Desktop/研究生/第二个想法(16s肠型&#xff0b;宏基因组功能)/第二篇病毒组/result/otherDB") data <- read.csv("feta…

【软件设计师知识点】一、计算机系统基础知识

文章目录 冯诺依曼计算机CPUCPU 的功能CPU 的组成 数据表示进制转换单位换算定点数浮点小数IEEE 754标准浮点数的运算 校验码奇偶校验码海明码循环冗余校验码&#xff08;CRC&#xff09; 指令系统指令格式寻址方式指令集指令流水线 存储系统存储器的层次化结构存储器的分类相联…

前端vue: 使用ElementUI适配国际化

i18n介绍 i18n&#xff08;其来源是英文单词 internationalization的首末字符i和n&#xff0c;18为中间的字符数&#xff09;是“国际化”的简称。 前端国际化步骤 1、安装i18n插件 安装插件时候&#xff0c;注意必须指定版本号&#xff0c;不然安装会报错。 npm i vue-i1…

【opencv】示例-points_classifier.cpp 使用不同机器学习算法在二维空间中对点集进行分类...

#include "opencv2/core.hpp" // 包含OpenCV核心功能的文件 #include "opencv2/imgproc.hpp" // 包含OpenCV图像处理功能的文件 #include "opencv2/ml.hpp" // 包含OpenCV机器学习模块的文件 #include "opencv2/highgui.hpp" // 包含O…

docker部署Prometheus+AlertManager实现邮件告警

文章目录 一、环境准备1、硬件准备&#xff08;虚拟机&#xff09;2、关闭防火墙&#xff0c;selinux3、所有主机安装docker 二、配置Prometheus1、docker启动Prometheus 三、添加监控节点1、docker启动node-exporter 四、Prometheus配置node-exporter1、修改prometheus.yml配置…

使用Python实现自动化网页答题功能-模拟考试篇

介绍 在驾驶员考试网站上进行模拟考试python自动答题 自动化原理 该脚本使用了自动化模块 DrissionPage 中的 ChromiumPage 类来实现网页的自动化操作。通过定位网页元素和模拟点击操作&#xff0c;完成了选择答案和提交答卷的过程。 用途与注意事项 用途&#xff1a;该脚本…

kafka学习笔记03

SpringBoot2.X项目搭建整合Kafka客户端依赖配置 用自己对应的jdk版本。 先加上我们的web依赖。 添加kafka依赖: SpringBoot2.x整合Kafka客户端adminApi单元测试 设置端口号。 新建一个kafka测试类&#xff1a; 创建一个初始化的Kafka服务。 设置kafka的名称。 测试创建kafka。…

在vue中配置样式 max-width:100px时,发现和width:100px一样没有对应的递增到最大宽度的效果?怎么回事?怎么解决?

原因&#xff1a; 可能时vue的样式大部分和display相关&#xff0c;有很多的联系&#xff0c;导致不生效 解决&#xff1a; 对设置max-width样式的元素设置display:inline-block;属性&#xff0c;即可生效&#xff0c;实现随着子元素的扩展而扩展并增加固定到最大的宽度

使用Postman发送跨域请求实验

使用Postman发送跨域请求 1 跨域是什么&#xff1f;2 何为同源呢?3 跨域请求是如何被检测到的&#xff1f;4 Postman跨域请求测试4.1 后端准备4.2 测试用例4.2.1 后端未配置跨域请求(1) 前端不跨域&#xff08;2&#xff09;前端跨域 4.2.2 后端配置跨域信息&#xff08;1&…