Singleton单例设计模式详解

目录

  • 模式定义
  • 应用场景
  • 实现方式
    • 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); //truenew 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); //truenew 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); //falseInnerClassSingleton 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.mzph.cn/news/814356.shtml

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

相关文章

pytorch车牌识别

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

Chatgpt掘金之旅—有爱AI商业实战篇|播客剧本写作|(十三)

演示站点&#xff1a; https://ai.uaai.cn 对话模块 官方论坛&#xff1a; www.jingyuai.com 京娱AI 一、AI技术创业播客剧本写作服务有哪些机会&#xff1f; 人工智能&#xff08;AI&#xff09;技术作为当今科技创新的前沿领域&#xff0c;为创业者提供了广阔的机会和挑战。…

b站江科大stm32笔记(持续更新)

b站江科大stm32笔记&#xff08;持续更新&#xff09; 片上资源/外设引脚定义表启动配置推挽开漏oc/od 门漏极/集电极 电阻的上拉下拉输入捕获输入捕获通道主从触发模式输入捕获基本结构PWMI基本结构PWMPSC ARR CRR输入捕获模式测频率TIM_PrescalerConfig()初始化输入捕获测频法…

mysql四种引擎区别

MySQL 提供了多种不同的数据库引擎&#xff0c;其中最常见的有 MyISAM、InnoDB、MEMORY 和 BLACKHOLE。这四个引擎分别有以下特点&#xff1a; 1. MyISAM MyISAM 是 MySQL 的默认引擎。它对于只有较少的修改、大量读取的应用场景具有良好的性能。它不支持事务处理&#xff0c;也…

Steam平台游戏发行流程

Steam平台游戏发行流程 大家好我是艾西&#xff0c;一个做服务器租用的网络架构师也是游戏热爱者&#xff0c;经常在steam平台玩各种游戏享受快乐生活。去年幻兽帕鲁以及雾锁王国在年底横空出世&#xff0c;幻兽帕鲁更是在短短一星期取得了非常好的成绩&#xff0c;那么作为游戏…

你的系统是如何跟MySQL打交道的

1、Java 工程师眼中的数据库是什么东西? 从今天开始&#xff0c;我们将要开始一个MySQL的专栏&#xff0c;一起来研究MySQL数据库的底层原理和各种实践案例&#xff0c;以及互联网公司的技术方案。 现在我们先来看看&#xff0c;在一个Java工程师眼中的数据库是什么东西? 平时…

【Godot4.2】CanvasItem绘图函数全解析 - 5.绘制字符和字符串

概述 到这一节为止&#xff0c;我们已经学习了如何在CanvasItem中绘制简单几何图形、图片以及样式盒。但是对于很重要的文字一直没有涉及。 本节就来讲一下字符和字符串绘制函数&#xff0c;以及替换它们的两个类。 系列目录 0.概述1.绘制简单图形2.设定绘图变换3.绘制纹理4…

解决vue3更新chunk包后,点击页面报错

出现错误 解决思路 试了好多方法&#xff0c;跳了很多坑&#xff0c;router版本对不上&#xff0c;解决方案不实用。最后我直接捕获异常&#xff0c;刷新页面&#xff0c;解决最快最有效。 // vue-rotuer版本 "vue-router": "^4.0.3"解决方案 在router/…

路由器配置实验--R1---R5

R1的路由表中默认存在:192.168.1.0192.168.3.0 需要添加:192.168.2.0 4.0 5.0 R2的路由表中默认存在:192.168.1.0192.168.2.0需要添加:192.168.3.0 4.0 5.0 R3的路由表中默认存在:192.168.3.0192.168.4.0需要添加: 1.0 2.0 5.0 R4的路由表中默认存在:192.168.2.0 192.168.4.0…

面试算法-164-K 个一组翻转链表

题目 给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。 你不能只是单纯的改变节点内…

深入微服务框架:构建高效、可扩展与弹性的现代应用架构

前言&#xff1a;当今快速迭代和多变的商业环境中&#xff0c;传统的单体应用程序面临着一系列挑战&#xff0c;包括难以管理复杂性、缺乏灵活性以及无法有效扩展等问题。随着业务需求的不断增长和技术栈的不断演进&#xff0c;企业亟需一种更加模块化、易于管理和扩展的应用程…

给你的AppImage创建桌面快捷方式

原文链接 https://www.cnblogs.com/HGNET/p/16396589.html 运行环境:Ubuntu 22.04 LTS 1.首先准备好AppImage文件并放在一个你知道的地方 2.打开终端&#xff0c;在/usr/share/applications下新建APP.desktop文件&#xff08;APP可以改成你的应用名称&#xff09; cd /usr/s…

在 Elasticsearch 中扩展 ML 推理管道:如何避免问题并解决瓶颈

作者&#xff1a;来自 Elastic Iulia Feroli 是时候考虑语义搜索运营了吗&#xff1f; 无论你是一位经验丰富的搜索工程师&#xff0c;希望探索新的人工智能功能&#xff0c;还是一位机器学习专家&#xff0c;希望更多地利用搜索基础设施来增强语义相似性模型 —— 充分利用这…

易舟云财务软件免费版和专业版有什么区别?

文章目录 1、价格&#xff08;1&#xff09;免费版&#xff08;2&#xff09;专业版 2、版本功能&#xff08;1&#xff09;免费版&#xff08;2&#xff09;专业版 1、价格 &#xff08;1&#xff09;免费版 永久免费&#xff01; &#xff08;2&#xff09;专业版 298元/…

笔记本台式机电脑 “睡眠和休眠”有什么区别,那个更省电

笔记本台式机电脑 Windows 系统里睡眠和休眠有什么区别&#xff0c;睡眠和休眠那个更省电&#xff0c;睡眠和休眠使用那个更好&#xff0c;当不用电脑时&#xff0c;通常有三种方式让电脑休息&#xff1a;关机、睡眠和休眠。关机的定义大家都懂&#xff0c;但睡眠和休眠就容易让…

OpenHarmony应用集成和固件集成中C库差异化分析

背景 OpenHarmony中&#xff0c;三方库的使用有两种方式&#xff1a; 一、固件集成 三方库经由OpenHarmony构建框架编译出的动态库或静态库&#xff0c;打包到rom中 二、应用集成 三方库经由IDE&#xff08;通过IDE中的cmake&#xff09;编译出的动态库或静态库&#xff0…

kafka学习笔记03

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

MySQL-进阶篇-基础架构:一条sql查询语句是如何执行的

摘自&#xff1a;01 | 基础架构&#xff1a;一条SQL查询语句是如何执行的&#xff1f;-MySQL实战45讲-极客时间 视频讲解&#xff1a;7分钟精通MySql中SQL执行原理_哔哩哔哩_bilibili 可结合学习&#xff0c;本文仅记录SQL语句的执行流程&#xff0c;以上内容有一些额外知识未…

学习MQ异步

1.MQ异步调用的优势 事件驱动模式&#xff1a; 优势&#xff1a; 总结&#xff1a; 2.初识MQ 核心概念以及结构&#xff1a; 常见的消息模型&#xff1a; 基本消息队列模型&#xff1a; 生产者代码&#xff1a; Testpublic void testSendMessage() throws IOException, Timeo…

C语言之_Generic用法实例(九十一)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…