Singleton单例设计模式详解

目录

  • 模式定义
  • 应用场景
  • 实现方式
  • 部分源码中的应用定位
    • 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.ppmy.cn/server/2848.html

相关文章

利用selenium发挥vip残存的价值

历史版本谷歌浏览器驱动下载地址 https://chromedriver.storage.googleapis.com/index.html 找到与你电脑当前谷歌浏览器版本一致的驱动然后下载下来(大版本一致即可)。我本地版本是 99.0.04844.51 我这里把 chromedriver 放到 /usr/local/bin 下面了。 启动测试窗口 这里需要…

基于STM32F103单片机的时间同步项目

一、前言 本项目为前一个时间同步项目的更迭版本&#xff0c;由于之前的G031开发板没有外部晶振&#xff0c;从机守时能力几乎没有&#xff0c;5秒以上不同步从机时间就开始飞了。在考虑成本选型后&#xff0c;选择了带有外部有缘晶振的STM32F103C8T6最小单片机&#xff0c;来作…

OSPF动态路由实验(华为)

思科设备参考&#xff1a;OSPF动态路由实验&#xff08;思科&#xff09; 一&#xff0c;技术简介 OSPF&#xff08;Open Shortest Path First&#xff09;是一种内部网关协议&#xff0c;主要用于在单一自治系统内决策路由。它是一种基于链路状态的路由协议&#xff0c;通过…

[尚硅谷flink] 检查点笔记

在Flink中&#xff0c;有一套完整的容错机制来保证故障后的恢复&#xff0c;其中最重要的就是检查点。 文章目录 11.1 检查点11.1.1 检查点的保存1&#xff09;周期性的触发保存2&#xff09;保存的时间点3&#xff09;保存的具体流程 11.1.2 从检查点恢复状态11.1.3 检查点算法…

鸢尾花数据集的KNN探索与乳腺癌决策树洞察

鸢尾花数据集的KNN探索与乳腺癌决策树洞察 今天博主做了这个KNN和决策树的实验。 一.数据集介绍 介绍一下数据集&#xff1a; 威斯康星州乳腺癌数据集&#xff1a; 威斯康星州乳腺癌数据集&#xff08;Wisconsin Breast Cancer Dataset&#xff09;是一个经典的机器学习数…

Jackson 2.x 系列【24】Spring Web 集成

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Jackson 版本 2.17.0 源码地址&#xff1a;https://gitee.com/pearl-organization/study-jaskson-demo 文章目录 1. 前言2. Spring Web3. Jackson2ObjectMapperBuilder4. Jackson2ObjectMapperFa…

Adobe Premiere Pro将加入AI生成式功能,以提高视频编辑的效率;OpenAI宣布在东京设立亚洲首个办事处

&#x1f989; AI新闻 &#x1f680; Adobe Premiere Pro将加入AI生成式功能&#xff0c;以提高视频编辑的效率 摘要&#xff1a;Adobe宣布&#xff0c;将为Premiere Pro引入由生成式AI驱动的新功能&#xff0c;以提高视频编辑的效率。这些功能包括“生成扩展”&#xff0c;能…

Mongodb入门--头歌实验MongoDB 实验——数据备份和恢复

在实际的应用场景中&#xff0c;经常需要对业务数据进行备份以做容灾准备&#xff0c; MongoDB 提供了备份和恢复的功能&#xff0c;分别是 MongoDB 下的 一、数据备份 任务描述 本关任务&#xff1a;按照编程要求备份数据库。 相关知识 为了完成本关任务&#xff0c;你需要掌…

Nginx 负载均衡配置

负载均衡算法 1. 轮询 权重 &#xff08;最为合理&#xff0c;常用&#xff09; 2. ip_hash / n取模&#xff08;n 节点个数&#xff09; &#xff08;移动端会因为网络&#xff0c;基站的变动&#xff0c;ip会变动。生产不推荐不用&#xff09; 3. 最少访问 &#xff08;记…

梯度下降法法实现线性回归模型

一、线性回归模型 线性回归模型是一种预测性的建模技术&#xff0c;它研究的是因变量&#xff08;目标&#xff09;和自变量&#xff08;特征&#xff09;之间的关系。这种关系假设是线性的&#xff0c;意味着因变量可以通过一个或多个自变量的线性组合来预测。数学上&#xf…

解决reactNative在Android中运行找不到模拟器

排查了半天&#xff0c;发现是环境配置错了 react native是根据一个叫ANDROID_HOME的环境变量寻找的&#xff0c;这个环境变量指向到sdk目录 重新排查的时候发现这个地址配置错了

【随笔】Git 高级篇 -- 获取远程分支数据 git fetch(二十七)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

Java面试 Day03

接口和抽象类有什么区别static和final有什么区别JVM加载类如何索引优化MySQL采用什么结构存储索引 为什么搜索算法有了解吗线程同步的几个方案&#xff0c;以及原理final关键词JVM调优&#xff0c;OOM经历隔离级别&#xff0c;InnoDB中几个隔离级别的原理Linux常用命令Redis可靠…

将闲置的windows硬盘通过smb共享的方式提供给mac作为时间机器备份

1.windows端&#xff0c;开启smb共享 自行解决 2.mac端 磁盘工具-文件-新建映像-空白映像 假设你的名字为&#xff1a;backup 大小&#xff1a;350GB&#xff08;自己修改&#xff09; 格式&#xff1a;MacOS扩展&#xff08;日志式&#xff09; 分区&#xff1a;单个分区-A…

【技术支持】禁止html中referer

如果页面中包含了如下 meta 标签&#xff0c;所有从当前页面中发起的请求将不会携带 referer&#xff1a; <meta name"referrer" content"never"> 如果页面中包含了如下 meta 标签&#xff0c;则从当前页面中发起的 http请求将只携带 origin 部分&a…

客户端传日期格式字段(String),服务端接口使用java.util.Date类型接收报错问题

客户端传日期格式字段&#xff08;string&#xff09;,服务端接口使用java.util.Date类型接收报错问题 问题演示第1种&#xff1a;客户端以URL拼接的方式传值第2种&#xff1a;客户端以body中的form-data方式提交第3种 客户端以Body中的json方式提交 问题解决&#xff08;全局解…

.NET 设计模式—享元模式(Flyweight Pattern)

简介 享元模式&#xff08;Flyweight Pattern&#xff09;是一种结构型设计模式&#xff0c;它旨在减少系统中相似对象的内存占用或计算开销&#xff0c;通过共享相同的对象来达到节省资源的目的。 享元模式提供了一种高效地共享对象的方式&#xff0c;从而减少了内存占用和提…

某国内大型零售企业供应链数据中台,让成本能够全盘掌握!

对于零售企业来说&#xff0c;供应链关乎企业命脉&#xff0c;决定了企业能否长久。 不夸张的说&#xff0c;新零售时代的竞争&#xff0c;就是供应链之间的竞争。但是很多零售企业的供应链管理水平参差不齐&#xff0c;成本浪费严重&#xff0c;出现了各种各样的管理漏洞。 …

微信小程序之自定义组件及使用

1、创建自定义组件文件夹与文件&#xff1a; 创建一个名为custom-dialog的自定义组件&#xff0c;位于components下&#xff0c;创建自定义弹窗组件文件夹结构如下&#xff1a; 写好自定义组件的结构文件.wxml、样式文件wxss、逻辑文件.js。 2、组件注册&#xff1a; 该组件…

JavaEE初阶Day 8:多线程(6)

目录 Day8&#xff1a;多线程&#xff08;6&#xff09;1. 内存可见性问题1.1 内存可见性问题介绍1.2 内存可见性问题解决 2. 线程的等待通知机制2.1 线程饿死2.2 等待通知机制2.2.1 wait2.2.2 notify Day8&#xff1a;多线程&#xff08;6&#xff09; 回顾 Ⅰ.可重入特性&a…