当前位置: 首页 > news >正文

1.单例模式

目录

简介

饿汉式

懒汉式

双重检测锁式

静态内部类式

枚举单例

测试

测试单例模式:

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

问题(拓展)

例:反射破解单例模式

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

总结:如何用


简介

单例对象(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.lryc.cn/news/436803.html

相关文章:

  • 数据倾斜问题
  • 大龄焦虑?老码农逆袭之路:拥抱大模型时代,焕发职业生涯新活力!
  • Vue 页面反复刷新常见问题及解决方案
  • Windows上指定盘符-安装WSL虚拟机(机械硬盘)
  • ffmpeg实现视频的合成与分割
  • 团体标准的十大优势
  • java spring boot 动态添加 cron(表达式)任务、动态添加停止单个cron任务
  • sqlgun靶场漏洞挖掘
  • 好用的 Markdown 编辑器组件
  • uniapp vite3 require导入commonJS 的js文件方法
  • 通义灵码用户说:“人工编写测试用例需要数十分钟,通义灵码以毫秒级的速度生成测试代码,且准确率和覆盖率都令人满意”
  • MySQL中的约束
  • Leetcode 寻找重复数
  • 大一新生以此篇开启你的算法之路
  • 【AI大模型】ChatGPT模型原理介绍(上)
  • 基于UE5和ROS2的激光雷达+深度RGBD相机小车的仿真指南(五):Blender锥桶建模
  • C++竞赛初阶L1-15-第六单元-多维数组(34~35课)557: T456507 图像旋转
  • 无线领夹麦克风哪个牌子好?西圣、罗德、猛犸领夹麦克风深度评测
  • React Native 0.76,New Architecture 将成为默认模式,全新的 RN 来了
  • Java并发:互斥锁,读写锁,Condition,StampedLock
  • 客户端负载均衡Ribbon实例
  • MySQL数据库负载均衡
  • 达梦CASE_SENSITIVE参数解析
  • 酒店智能轻触开关工作原理
  • web基础之RCE
  • c语言--水仙花数,求Sn的前五项和
  • SpringBoot教程(二十八) | SpringBoot集成Elasticsearch(Java High Level Rest Client方式)
  • 【Vue3】常用的响应式数据类型
  • 搭建本地DVWA靶场教程 及 靶场使用示例
  • 60. n 个骰子的点数【难】