1.单例模式

news/2024/9/18 14:49:14/ 标签: 单例模式

目录

简介

饿汉式

懒汉式

双重检测锁式

静态内部类式

枚举单例

测试

单例模式:-toc" style="margin-left:80px;">测试单例模式

单例模式在多线程环境下的效率-toc" style="margin-left:80px;">测试五种单例模式在多线程环境下的效率

问题(拓展)

单例模式-toc" style="margin-left:80px;">例:反射破解单例模式

单例模式-toc" style="margin-left:80px;">例:反序列化破解单例模式

总结:如何用


简介

单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个 JVM 中,该对象只有一个实例存在。这样的模式有几个好处:

单例模式的优点:

  • 由于单例模式只生产一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决
  • 单例模式可以在系统设置全局的访问点,优化环境共享资源访问,例如可以设计一个单例类,负责所以数据表的映射处理

常见的五种单例模式实现方式

  • 主要:
    • 饿汉式(线程安全,调用效率高;但是,不能延迟加载)
    • 懒汉式(线程安全,调用效率不高;但是,可以延迟加载)
  • 其他:
    • 双重检测锁式(由于 JVM 底层内部模型原因,偶尔会出问题,不建议使用)
    • 静态内部类式(线程安全,调用效率高;但是,可以延时加载)
    • 枚举单例(线程安全,调用效率高,不能延时加载)

饿汉式

/*** @ProjectName:* @Package:        com.example.gof23.creational_patterns.singleton* @ClassName:      SingletonHg* @Description:    饿汉式单例模式(Hungry)* @Author:         qfxl* @CreateDate:     2024/09/09 23:00* @Version:        1.0.0*/
public class SingletonHungry {//类初始化时,立即加载这个对象//(在类加载器加载时,是天然的线程安全模式;同时因为是立即加载,所以没有延迟加载的优势)private static SingletonHungry instance = new SingletonHungry();//私有化构造器private SingletonHungry() {}//方法没有同步,调用效率高public static SingletonHungry getInstance() {return instance;}
}

懒汉式

/*** @ProjectName:* @Package:        com.example.gof23.creational_patterns.singleton* @ClassName:      SingletonLazy* @Description:    懒汉式单例模式(Lazy)* @Author:         qfxl* @CreateDate:     2024/09/09 23:00* @Version:        1.0.0*/
public class SingletonLazy {//类初始化时,不初始化这个对象(实现懒加载 或者叫 延迟加载(lazy load),真正用到的时候才加载)private static SingletonLazy instance;//私有化构造器private SingletonLazy() {}//方法同步,调用效率低public static synchronized SingletonLazy getInstance() {if (instance == null) {instance = new SingletonLazy();}return instance;}
}

双重检测锁式

/*** @ProjectName:* @Package:        com.example.gof23.creational_patterns.singleton* @ClassName:      SingletonDC* @Description:    双重检测锁式单例模式(Double Checked Locking)* @Author:         qfxl* @CreateDate:     2024/09/09 23:10* @Version:        1.0.0*/
public class SingletonDC {//使用了volatile关键字后,重排序被禁止,所有的写(write)操作都将发生在读(read)操作之前private volatile static SingletonDC instance;//私有化构造器private SingletonDC() {}//双重检测锁式public SingletonDC getInstance() {if (instance == null) {synchronized (SingletonDC.class) {if (instance == null) {instance = new SingletonDC();}}}return instance;}
}

静态内部类式

/*** @ProjectName:* @Package:        com.example.gof23.creational_patterns.singleton* @ClassName:      SingletonSIC* @Description:    静态内部类式单例模式(Static Inner Class)* @Author:         qfxl* @CreateDate:     2024/09/09 23:10* @Version:        1.0.0*/
public class SingletonSIC {private static class SingletonClassInstance {private static final SingletonSIC instance = new SingletonSIC();}//私有化构造器private SingletonSIC() {}public static SingletonSIC getInstance(){return SingletonClassInstance.instance;}
}

枚举单例

/*** @ProjectName:* @Package:        com.example.gof23.creational_patterns.singleton* @ClassName:      SingletonEnum* @Description:    枚举类单例模式(Enum)* @Author:         qfxl* @CreateDate:     2024/09/09 23:10* @Version:        1.0.0*/
public enum SingletonEnum {//这个枚举元素,本身就是单例对象(没有延时加载)INSTANCE;//添加自己需要的操作public void singletonOperation() {}
}

测试

单例模式:">测试单例模式

public class Client {public static void main(String[] args) {//测试饿汉式单例模式(Hungry)SingletonHungry hungry1 = SingletonHungry.getInstance();SingletonHungry hungry2 = SingletonHungry.getInstance();System.out.println(hungry1);System.out.println(hungry2);//测试懒汉式单例模式SingletonLazy lazy1 = SingletonLazy.getInstance();SingletonLazy lazy2 = SingletonLazy.getInstance();System.out.println(lazy1);System.out.println(lazy2);//测试双重检测锁单例模式SingletonDC dc1 = SingletonDC.getInstance();SingletonDC dc2 = SingletonDC.getInstance();System.out.println(dc1);System.out.println(dc2);//测试静态内部类式单例模式SingletonSIC sic1 = SingletonSIC.getInstance();SingletonSIC sic2 = SingletonSIC.getInstance();System.out.println(sic1);System.out.println(sic2);//测试枚举单例模式SingletonEnum anEnum1 = SingletonEnum.INSTANCE;SingletonEnum anEnum2 = SingletonEnum.INSTANCE;System.out.println(anEnum1==anEnum2);}
}

结果为:

饿汉式:com.example.gof23.creational_patterns.singleton.SingletonHungry@1540e19d
饿汉式:com.example.gof23.creational_patterns.singleton.SingletonHungry@1540e19d
懒汉式:com.example.gof23.creational_patterns.singleton.SingletonLazy@677327b6
懒汉式:com.example.gof23.creational_patterns.singleton.SingletonLazy@677327b6
双重检测锁:com.example.gof23.creational_patterns.singleton.SingletonDC@14ae5a5
双重检测锁:com.example.gof23.creational_patterns.singleton.SingletonDC@14ae5a5
静态内部类:com.example.gof23.creational_patterns.singleton.SingletonSIC@7f31245a
静态内部类:com.example.gof23.creational_patterns.singleton.SingletonSIC@7f31245a
枚举单例:true

单例模式在多线程环境下的效率">测试五种单例模式在多线程环境下的效率

(关注相对值即可,不同的环境下测试值完全不一样)

五种单例模式时间
饿汉式(SingletonHungry)26ms
懒汉式(SingletonLazy)186ms
双重检测锁式(SingletonDC)40ms
静态内部类式(SingletonSIC)31ms
枚举单例(SingletonEnum)37ms
  • CountDownLatch
    • 同步辅助类,在完全一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待
    • countDown():当前线程调用此方法,则计数减一(建议放在 finally 里执行)
    • await():调用此方法会一直阻塞当前线程,直到计时器的值为0
public class ClientTimes {public static void main(String[] args) throws InterruptedException {long start = System.currentTimeMillis();int threadNum = 10;final CountDownLatch countDownLatch = new CountDownLatch(threadNum);for (int i = 0; i < threadNum; i++) {new Thread(new Runnable() {public void run() {for (int i = 0; i < 1000000; i++) {//分别对下面的单例模式进行测试SingletonHungry hungry = SingletonHungry.getInstance();
//                        SingletonLazy lazy = SingletonLazy.getInstance();
//                        SingletonDC dc = SingletonDC.getInstance();
//                        SingletonSIC sic = SingletonSIC.getInstance();
//                        SingletonEnum anEnum = SingletonEnum.INSTANCE;}countDownLatch.countDown();}}).start();}//main线程阻塞,直到计数器变为0,才会继续执行countDownLatch.await();long end = System.currentTimeMillis();System.out.println("总耗时:" + (end - start));}
}

测试结果如上表格

问题(拓展)

  • 反射可以破解上面几种(不包含枚举式)实现反式(可以在构造方法中手动抛出异常控制)
  • 反序列化可以破解上面几种(不包含枚举式)实现方式(可以通过定义 readResolver() 防止获得不同的对象)
    • 反序列化时,如果对象所在的类定义了 readResolver(),(实际是一种回调),定义返回那个对象

单例模式">例:反射破解单例模式

package com.example.gof23.creational_patterns.singleton.expand;public class SingletonDemo {private static SingletonDemo instance = new SingletonDemo();//私有化构造器private SingletonDemo() {}public static SingletonDemo getInstance() {return instance;}}

public class Test_reflect {public static void main(String[] args) throws Exception {//通过反射来破解单例模式(通过反射的方式直接调用私有化构造器)Class<SingletonDemo> clazz = (Class<SingletonDemo>) Class.forName("com.example.gof23.creational_patterns.singleton.expand.SingletonDemo");Constructor<SingletonDemo> c = clazz.getDeclaredConstructor(null);c.setAccessible(true);//跳过权限的检测,使其可以访问私有的方法SingletonDemo sd1 = c.newInstance();SingletonDemo sd2 = c.newInstance();System.out.println(sd1);System.out.println(sd2);}
}

结果为:

com.example.gof23.creational_patterns.singleton.expand.SingletonDemo@1540e19d
com.example.gof23.creational_patterns.singleton.expand.SingletonDemo@677327b6

防止反射破解单例模式:在构造方法中手动抛出异常控制

package com.example.gof23.creational_patterns.singleton.expand;public class SingletonDemo {private static SingletonDemo instance = new SingletonDemo();//私有化构造器private SingletonDemo() {//防止反射获取私有化的构造方法,从而破解单例模式if (instance != null) {throw new RuntimeException();}}public static SingletonDemo getInstance() {return instance;}}

再运行的结果为:

com.example.gof23.creational_patterns.singleton.expand.SingletonDemo@135fbaa4
com.example.gof23.creational_patterns.singleton.expand.SingletonDemo@135fbaa4

单例模式">例:反序列化破解单例模式

package com.example.gof23.creational_patterns.singleton.expand;import java.io.Serializable;public class SingletonDemo implements Serializable {private static SingletonDemo instance = new SingletonDemo();//私有化构造器private SingletonDemo() {}public static SingletonDemo getInstance() {return instance;}}

public class Test_serializable {public static void main(String[] args) throws Exception {//通过反序列化的方式构造多个对象SingletonDemo instance1 = SingletonDemo.getInstance();FileOutputStream fos = new FileOutputStream("e:/a.txt");ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeObject(instance1);FileInputStream fis = new FileInputStream("e:/a.txt");ObjectInputStream ois = new ObjectInputStream(fis);SingletonDemo instance2 = (SingletonDemo) ois.readObject();System.out.println(instance1);System.out.println(instance2);}
}

结果为:

com.example.gof23.creational_patterns.singleton.expand.SingletonDemo@135fbaa4
com.example.gof23.creational_patterns.singleton.expand.SingletonDemo@58372a00

防止反序列化破解单例模式:通过定义 readResolver() 防止获得不同的对象

package com.example.gof23.creational_patterns.singleton.expand;import java.io.Serializable;public class SingletonDemo implements Serializable {private static SingletonDemo instance = new SingletonDemo();//私有化构造器private SingletonDemo() {//防止反射获取私有化的构造方法,从而破解单例模式if (instance != null) {throw new RuntimeException();}}public static SingletonDemo getInstance() {return instance;}//在反序列化时,如果定义了此方法,则直接返回此方法中的对象,无需单独再创建新对象private Object readResolve() {return instance;}}

再运行结果为:

com.example.gof23.creational_patterns.singleton.expand.SingletonDemo@135fbaa4
com.example.gof23.creational_patterns.singleton.expand.SingletonDemo@135fbaa4

总结:如何用

  • 单例对象占用资源少,不需要延迟加载:
    • 枚举式 好于 饿汉式
  • 单例对象占用资源大,需要延迟加载:
    • 静态内部类式 好于 懒汉式

http://www.ppmy.cn/news/1525240.html

相关文章

【PyCharm】常用快捷键

此篇文章内容会不定期更新&#xff0c;仅作为学习过程中的笔记记录 PyCharm的所有快捷键&#xff0c;其实均可以自定义&#xff0c;在位于Settings -> Keymap的目录下&#xff08;如图&#xff09;&#xff0c;可以自行改写为自己熟悉的键位组合。 若更改为PyCharm已存在的键…

GESP等级考试 C++二级-if语句

if语句是C中的选择语句&#xff0c;通过if语句程序可以在一种可能、二种可能或者多种可能中做出选择&#xff0c;对于不同的可能进行不同的处理。 1 一种可能 使用if语句对一种可能进行处理的格式如下所示&#xff1a; if (表达式) {语句; } 其中&#xff0c;if后面跟一个圆…

虹科方案 | 精准零部件测试!多路汽车开关按键功能检测系统

欢迎关注虹科&#xff0c;为您提供最新资讯&#xff01; #LIN/CAN总线 #零部件测试 #CAN数据 导读 在汽车制造业中&#xff0c;零部件的安全性、功能性和可靠性是确保车辆整体性能的关键。虹科针对车辆零部件的LIN/CAN总线仿真测试&#xff0c;提出了基于虹科Baby-LIN系列产…

基于单片机的人脸识别的智能门禁系统设计

文章目录 前言资料获取设计介绍功能介绍设计清单核心代码具体实现截图参考文献设计获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等…

【Redis入门到精通一】什么是Redis?

目录 Redis 1. Redis的背景知识 2.Redis特性 3.Redis的使用场景 4.Ubuntu上安装配置Redis Redis Redis在当今编程技术中的地位可以说非常重要&#xff0c;大多数互联网公司内部都在使用这个技术&#xff0c;熟练使用Redis已经成为开发人员的一个必备技能。本章将带领读者…

oracle select字段有子查询会每次执行子查询吗

Oracle在执行SELECT语句时&#xff0c;如果子查询被嵌套在主查询中&#xff0c;子查询会被执行多次&#xff0c;这是因为子查询的结果不会被缓存。每次主查询需要用到子查询的结果时&#xff0c;子查询都会被重新执行。这种行为可能会导致性能问题&#xff0c;特别是当子查询结…

计算机网络:概述 - 计算机网络概述

目录 一. 互联网概述 1.1 网络 1.2 互联网 1.3 因特网 二. 互联网发展的三个阶段 三. 互联网的标准化工作 四. 互联网的组成 五. 计算机网络的类别 5.1 计算机网络的定义 5.2 计算机网络的不同类别 一. 互联网概述 起源于美国的互联网现如今已…

golang学习笔记13——golang的错误处理深度剖析

推荐学习文档 golang应用级os框架&#xff0c;欢迎star基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总golang学习笔记01——基本数据类型golang学习笔记02——gin框架及基本原理golang学习笔记03——gin框架的核心数据结构golang学…

简单了解 JVM

目录 ♫什么是JVM ♫JVM的运行流程 ♫JVM运行时数据区 ♪虚拟机栈 ♪本地方法栈 ♪堆 ♪程序计数器 ♪方法区/元数据区 ♫类加载的过程 ♫双亲委派模型 ♫垃圾回收机制 ♫什么是JVM JVM 是 Java Virtual Machine 的简称&#xff0c;意为 Java虚拟机。 虚拟机是指通过软件模…

新手制作视频用什么软件好?五款剪辑工具分享!

在数字时代&#xff0c;视频制作已成为许多人表达创意、记录生活的重要方式。但对于新手而言&#xff0c;面对琳琅满目的视频编辑软件&#xff0c;往往会感到无所适从。今天&#xff0c;我们就来推荐五款适合新手的视频制作软件&#xff0c;它们分别适用于不同的操作系统平台&a…

探索 Linux:开源操作系统的璀璨世界

摘要&#xff1a;本文围绕 Linux 展开深入探讨。从历史来看&#xff0c;20 世纪 90 年代初 Linus Torvalds 发布 Linux 内核源代码开启新纪元&#xff0c;开源模式使内核不断成长。Linux 的核心概念包含内核、文件系统、进程和线程等&#xff0c;其中内核管理硬件资源与提供系统…

Win10安装.net FrameWork3.5失败解决方法

win10安装.net FrameWork3.5失败解决方法 已经好久没有来投稿了,实在最近业务缠身,忙的焦头烂额(呵~多么伟大的牛马) 但最近开发使用windows11实在是拉胯的不行,升级完就后悔,所以就一怒之下,重装了win10 可是,好家伙,我重装完遇到一个问题,就是在使用.Net Framework3.5,按照Mi…

物理学基础精解【5】

文章目录 质点的速度一、平均速度二、瞬时速度三、速度质点的速度计算一、平均速度的计算二、瞬时速度的计算三、速度矢量的标量分量性质、定义、计算、例子和例题性质定义计算例子例题 质点的瞬时速度的方向的计算、性质、例子和例题计算性质例子例题 质点的速度 本题主要考察…

可对画面进行平台传输,实时查看监控的智慧交通开源了

智慧交通视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。用户只需在界面上…

C语言字符函数与字符串函数

目录 1. 字符函数 1.1 字符分类函数 1.2 字符转换函数 2. 字符串函数 2.1 strlen 函数 2.2 strcpy 函数 2.3 strcat 函数 2.4 strcmp 函数 2.5 strncpy 函数 2.6 strncat 函数 2.7 strncmp 函数 2.8 strstr 函数 结语 1. 字符函数 在C语言标准库中提供了一系列用于…

RocketMQ 消费方式

在消息传递系统中&#xff0c;“推&#xff08;Push&#xff09;”和“拉&#xff08;Pull&#xff09;”是两种不同的消息消费方式&#xff0c;RocketMQ 也支持这两种模式。下面是对这两种模式的详细解释&#xff1a; 1. 推模式&#xff08;Push Model&#xff09; 模式简介…

【机器学习(五)】分类和回归任务-AdaBoost算法-Sentosa_DSML社区版

文章目录 一、算法概念一、算法原理&#xff08;一&#xff09;分类算法基本思路1、训练集和权重初始化2、弱分类器的加权误差3、弱分类器的权重4、Adaboost 分类损失函数5、样本权重更新6、AdaBoost 的强分类器 &#xff08;二&#xff09;回归算法基本思路1、最大误差的计算2…

智慧环保平台建设方案

智慧环保平台建设方案摘要 政策导向与建设背景 背景&#xff1a;全国生态环境保护大会提出坚决打好污染防治攻坚战&#xff0c;推动生态文明建设&#xff0c;目标是在2035年实现生态环境质量根本好转。构建生态文明体系&#xff0c;包括生态文化、生态经济、目标责任、生态文明…

【每日一题】LeetCode 2398.预算内的最多机器人数目(滑动窗口、数组、二分查找、前缀和、堆(优先队列))

【每日一题】LeetCode 2398.预算内的最多机器人数目&#xff08;滑动窗口、数组、二分查找、前缀和、堆&#xff08;优先队列&#xff09;&#xff09; 题目描述 给定两个整数数组 chargeTimes 和 runningCosts&#xff0c;分别代表 n 个机器人的充电时间和运行成本。再给定一…

通过docker overlay2目录名查找容器名和容器ID

有时候经常会有个别容器占用磁盘空间特别大&#xff0c;这个时候就需要通过docker overlay2 日录名查找对应容器名. 1.首先进入到 /var/lib/docker/overlay2 目录下 # cd /var/lib/docker/overlay2 2.查看谁占用容间最大 # du -h -d 1 | grep G |sort -nr 3.再通过目录名查找…