我眼中的单例模式

ESP32-CAM与TFT屏幕实时图像传输:Arduino环境下的OV2640驱动实践 本文详细介绍了在Arduino环境下实现ESP32-CAM与TFT屏幕实时图像传输的完整实践方案。重点解析了OV2640摄像头的驱动配置、硬件引脚连接、TFT屏幕初始化及图像显示优化技巧,帮助开发者快速构建稳定的实时图像显示系统,适用于物联网监控、门禁系统等场景。 阅读详情

单例设计模式

1、饿汉式

这种其实大家都懂,不多说,上代码。

package singleton;
public class Singleton1 {
   private static Singleton1 instance = new Singleton1();
   private Singleton1 (){}
   public static Singleton1 getInstance() {
       return instance;
   }
}
优点就是线程安全啦,缺点很明显,类加载的时候就实例化对象了,浪费空间。于是乎,就提出了懒汉式的单例模式

2、懒汉式

(1)懒汉式v1
package singleton;
public class LazySingleton1 {
   private static LazySingleton1 instance;
   private LazySingleton1 (){}
   public static LazySingleton1 getInstance() {
       if (instance == null) {
           instance = new LazySingleton1();
       }
       return instance;
   }

然而这一版线程是不安全的,于是乎为了线程安全,就在getInstance()方法上加synchronized修饰符,于是getInstance()方法如下所示

public static synchronized LazySingleton1 getInstance() {
       if (instance == null) {
           instance = new LazySingleton1();
       }
       return instance;
   }

然而,将synchronized加在方法上性能大打折扣(syncrhonized会造成线程阻塞),于是乎又提出一种双重校验锁的单例设计模式,既保证了线程安全,又提高了性能。双重校验锁的getInstance()方法如下所示

public static LazySingleton1 getInstance() {
       if (instance == null) {  
           synchronized (LazySingleton1.class) {  
           if (instance == null) {  
               instance = new LazySingleton1();  
               }  
           }  
       }
       return instance;
   }


(2)懒汉式v2

懒汉式v1的最后一个双重校验锁版,不管性能再如何优越,还是使用了synchronized修饰符,既然使用了该修饰符,那么对性能多多少少都会造成一些影响,于是乎懒汉式v2版诞生。不过在讲该版之前,我们先来复习一下内部类的加载机制,代码如下

package test;
public class OuterTest {
   static {
       System.out.println("load outer class...");
   }
   // 静态内部类
   static class StaticInnerTest {
       static {
           System.out.println("load static inner class...");
       }
       static void staticInnerMethod() {
           System.out.println("static inner method...");
       }
   }
   public static void main(String[] args) {
       OuterTest outerTest = new OuterTest(); // 此刻其内部类是否也会被加载?
       System.out.println("===========分割线===========");
       OuterTest.StaticInnerTest.staticInnerMethod(); // 调用内部类的静态方法
   }
}

输出如下

load outer class...
===========分割线===========
load static inner class...
static inner method

因此,我们有如下结论

  1. 加载一个类时,其内部类不会同时被加载。

  2. 一个类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生。。

基于上述结论,我们有了懒汉式V2版,代码如下所示

package singleton;
public class LazySingleton2 {
   private LazySingleton2() {
   }
   static class SingletonHolder {
       private static final LazySingleton2 instance = new LazySingleton2();
   }
   public static LazySingleton2 getInstance() {
       return SingletonHolder.instance;
   }
}

由于对象实例化是在内部类加载的时候构建的,因此该版是线程安全的(因为在方法中创建对象,才存在并发问题,静态内部类随着方法调用而被加载,只加载一次,不存在并发问题,所以是线程安全的)。

另外,在getInstance()方法中没有使用synchronized关键字,因此没有造成多余的性能损耗。当LazySingleton2类加载的时候,其静态内部类SingletonHolder并没有被加载,因此instance对象并没有构建。

而我们在调用LazySingleton2.getInstance()方法时,内部类SingletonHolder被加载,此时单例对象才被构建。因此,这种写法节约空间,达到懒加载的目的,该版也是众多博客中的推荐版本。

ps:其实枚举单例模式也有类似的性能,但是因为可读性的原因,并不是最推荐的版本。

(3)懒汉式v3

然而,懒汉式v2版在反射的作用下,单例结构是会被破坏的,测试代码如下所示

package test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import singleton.LazySingleton2;
/**
* @author zhengrongjun
*/

public class LazySingleton2Test {
   public static void main(String[] args) {
       //创建第一个实例
       LazySingleton2 instance1 = LazySingleton2.getInstance();
       //通过反射创建第二个实例
       LazySingleton2 instance2 = null;
       try {
           Class<LazySingleton2> clazz = LazySingleton2.class;
           Constructor<LazySingleton2> cons = clazz.getDeclaredConstructor();
           cons.setAccessible(true);
           instance2 = cons.newInstance();
       } catch (Exception e) {
           e.printStackTrace();
       }
       //检查两个实例的hash值
       System.out.println("Instance 1 hash:" + instance1.hashCode());
       System.out.println("Instance 2 hash:" + instance2.hashCode());
   }
}

输出如下

Instance 1 hash:1694819250
Instance 2 hash:1365202186

根据哈希值可以看出,反射破坏了单例的特性,因此懒汉式V3版诞生了

package singleton;
public class LazySingleton3 {
   private static boolean initialized = false;
   private LazySingleton3() {
       synchronized (LazySingleton3.class) {
           if (initialized == false) {
               initialized = !initialized;
           } else {
               throw new RuntimeException("单例已被破坏");
           }
       }
   }
   static class SingletonHolder {
       private static final LazySingleton3 instance = new LazySingleton3();
   }
   public static LazySingleton3 getInstance() {
       return SingletonHolder.instance;
   }
}

此时再运行一次测试类,出现如下提示

java.lang.reflect.InvocationTargetException
   at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
   at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
   at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
   at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
   at test.LazySingleton3Test.main(LazySingleton3Test.java:21)
Caused by: java.lang.RuntimeException: 单例已被破坏
   at singleton.LazySingleton3.<init>(LazySingleton3.java:12)
   ... 5 more
Instance 1 hash:359023572

这里就保证了,反射无法破坏其单例特性

(3)懒汉式v4

在分布式系统中,有些情况下你需要在单例类中实现 Serializable 接口。这样你可以在文件系统中存储它的状态并且在稍后的某一时间点取出。

让我们测试这个懒汉式V3版在序列化和反序列化之后是否仍然保持单例。

先将

public class LazySingleton3

修改为

public class LazySingleton3 implements Serializable 

上测试类如下

package test;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import singleton.LazySingleton3;
public class LazySingleton3Test {
   public static void main(String[] args) {
       try {
           LazySingleton3 instance1 = LazySingleton3.getInstance();
           ObjectOutput out = null;
           out = new ObjectOutputStream(new FileOutputStream("filename.ser"));
           out.writeObject(instance1);
           out.close();
           //deserialize from file to object
           ObjectInput in = new ObjectInputStream(new FileInputStream("filename.ser"));
           LazySingleton3 instance2 = (LazySingleton3) in.readObject();
           in.close();
           System.out.println("instance1 hashCode=" + instance1.hashCode());
           System.out.println("instance2 hashCode=" + instance2.hashCode());
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
}

输出如下

instance1 hashCode=2051450519
instance2 hashCode=1510067370

显然,我们又看到了两个实例类。为了避免此问题,我们需要提供 readResolve() 方法的实现。readResolve()代替了从流中读取对象。这就确保了在序列化和反序列化的过程中没人可以创建新的实例。

因此,我们提供懒汉式V4版代码如下

package singleton;
import java.io.Serializable;
public class LazySingleton4 implements Serializable {
   private static boolean initialized = false;
   private LazySingleton4() {
       synchronized (LazySingleton4.class) {
           if (initialized == false) {
               initialized = !initialized;
           } else {
               throw new RuntimeException("单例已被破坏");
           }
       }
   }
   static class SingletonHolder {
       private static final LazySingleton4 instance = new LazySingleton4();
   }
   public static LazySingleton4 getInstance() {
       return SingletonHolder.instance;
   }
   private Object readResolve() {
       return getInstance();
   }
}

此时,在运行测试类,输出如下

instance1 hashCode=2051450519
instance2 hashCode=2051450519

这表示此时已能保证序列化和反序列化的对象是一致的

总结

本文给出了多个版本的单例模式,供我们在项目中使用。实际上,我们在实际项目中一般从懒汉式v2、懒汉式v3、懒汉式v4中,根据实际情况三选一即可,并不是非要选择懒汉式v4作为单例来实现。最后,希望大家有所收获。

Qt C++ + Qt 3D 的数字孪生工厂 以下是一个基于 **Qt C++ + Qt 3D** 的数字孪生工厂完整源码示例,集成了 **Qt 3D** 产线建模和 **WebSocket** 实时数据交互。ui->qualifiedProductsLabel->setText(QString("合格产品: %1").arg(data.qualifiedProducts));ui->totalProductsLabel->setText(QString("总产量: %1").arg(data.totalProducts)); 阅读详情

相关推荐

安卓 实现灯光闪过效果

有个小需求,给按钮加一个灯光划过的效果,这样的需求一般有UI,肯定就找UI了要个gif图不就完事了,可是这次没有,好久自己做一个嘛。Web端的大佬实现很快,到安卓这边呢,百度一搜发现诶?怎么没有,好吧自己来一个 一切动效相关的不给图看都是耍流氓 所以俺不能这么干啊,OK!直接上效果图,再来解释下怎么实现的 效果就是这样子,怎么实现呢? 从图分析下,就是一个光写着重左边移动要右边循环嘛,这有啥难的?? 来看一看我的第一版,第二版 这是个什么?????一点都不像啊? 赶紧再问了问Web端大佬,噢,加了一个

SummerCloudXT的博客 1378

设计模式 - 单例模式的5种实现方法

确保类在程序中只有一个实例,并提供一个全局访问点。

逆风飞翔的博客 712

运算放大器电路仿真实践:从零实现完整示例

通过一个完整的运算放大器电路仿真示例,深入掌握电路仿真的关键步骤与技巧,帮助电子爱好者和工程师快速上手实际项目设计。

weixin_34725745的博客 1319

冲鸭!33% 程序员月薪达到 5 万元以上

????????关注后回复“进群”,拉你进程序员交流群????????来自拉勾数据报告2023年,随着互联网产业的蓬勃发展,程序员作为一个自带“高薪多金”标签的热门群体,被越来越多的人所关注。在过去充满未知的一年中,他们的职场现状发生了一定的改变。那么,程序员岗位的整体薪资水平、婚恋现状、职业方向等相较于前两年都发生了怎样的变化呢??近期,拉勾发布了一份《2022程序员群体职场洞察报告》(以下简称报告),向我们展露了...

<sdffdsfsdfdfs>sfsfsfsdfsdffds</sdfsDS>Fsd 282

Verilog基础知识11(异步FIFO为什么使用格雷码(gray-code) )

需求说明:Verilog设计 内容       :第一部分 异步FIFO为什么使用格雷码                   第二部分 格雷码在异步FIFO中的使用 来自       :时间的诗 第一部分 异步FIFO为什么使用格雷码 原文:http://www.cnblogs.com/kxk_kxk/p/3931591.html 异步FIFO通

时间的诗 8888

如何选择深度强化学习算法:MuZero/SAC/PPO/TD3/DDPG/DQN/等算法

如何选择深度强化学习算法:MuZero/SAC/PPO/TD3/DDPG/DQN/等算法

丨汀、的博客 5222

程序员的7个被动收入途径——我如何每月赚 5万

程序员的7个被动收入途径——我如何每月赚 5万

一名正义的白帽黑客 1878

自助结账商品检测(Scanned Item Detection)——基于YOLOv8的RPC数据集实战

RPC数据集是由中国科学院自动化研究所发布的零售商品结账检测公开数据集,广泛用于自助结账和零售场景下的目标检测与识别。YOLOv8是YOLO系列最新版本,由Ultralytics推出,继承了YOLO家族的高效快速和高准确性,适用于实时检测场景。本文系统介绍了基于YOLOv8的RPC数据集自助结账商品检测方案,涵盖数据准备、训练、推理及UI界面设计。未来,可结合多模态感知、RFID融合技术,打造更加智能和稳定的自助结账系统。

YOLO 252

终于把单例模式讲详细了吧

单例模式 饿汉式: /** * 饿汉式单例(提前把对象创建) * 可能会浪费空间,提前把对象创建好了,但是不一定会用。 */ public class Hungry { private Hungry(){ } private final static Hungry HUNGRY=new Hungry(); public static Hungry g...

l13kddd的博客 1745

Flatten 层的深度理解与运用

Flatten 层的深度理解与运用

你要拥有一个你喜欢并且热爱的人生 7034

结构光之相移法+多频外差的数学原理推导

‍论文:基于数字光栅投影的结构光三维测量技术与系统研究 [博]作者:李中伟 华中科技大学 2007功能:相移法:多频外差(三频N步相移法)01 相移法图1 待写入投影仪的理想N步相移条纹通常对于 步相移来说,生成第 幅条纹图像(如图1所示)的公式如下:402 Payment Required其中::背景光强:调制强度:像素点横向坐标:相移值图2 相机拍摄到的实际 N ......

3D视觉工坊 1189

设计模式(五)——单例模式

单例模式,主要由以下几个模块组成:私有的静态对象实例,私有的构造方法(避免外部调用new对象,保证只有一个对象的实例),一个共有的静态获取对象的方法(供外部调用)。 代码如下: 懒汉式单例模式(在需要的时候,也即调用共有的静态获取对象的方法时才创建对象): package SinglePattern; public class LazySingle { private stat...

Dream_Ryoma的博客 616

livox雷达斜装修改

指的是,使用外部的陀螺仪的话,雷达到外部陀螺仪的转换,而mid360的陀螺仪是内置在360里面的,且方向和点云方向是一致的,所以不能通过修改此处去改变陀螺仪方向。同时livox_ros_driver2中的MID360_config.json里面的。这里面的外参,只能改变点云相对陀螺仪的位姿,而不能将陀螺仪映射到你期望的倾斜角度。fast_lio中的mid360.yaml中的外参。

z1872385的博客 2712

我眼中的单例模式并不完美

/// <summary> /// 全局唯一的配置信息 /// </summary> public class Config { private static Config _config = null; public static Config GetConfig() { if (_config == null) { _co.

架构师修行之路 1067

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

牛逼程序员眼中的单例模式

作者:孤独烟,来自:http://rjzheng.cnblogs.com/引言其实写这篇文章之前,我犹豫了一下,毕竟单例大家都知道,写这么一篇文章会不会让人觉得老掉牙。后来想想,就当一种记录吧。先来一副漫画吧,如下图所示ok,我们回顾下小灰的遭遇,上述漫画所提出的那些问题主要有以下三点:为什么静态内部类的单例模式是最推荐的?如何在反射的情况下保证单例?如何在反序列化中保证单例?针对上述三个问题有了...

miwanmeng的博客 279

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

月薪5万程序员眼中的单例模式

作者:孤独烟,来自:http://rjzheng.cnblogs.com/引言其实写这篇文章之前,我犹豫了一下,毕竟单例大家都知道,写这么一篇文章会不会让人觉得老掉牙。后...

架构师小秘圈 1718

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

程序员羽化之路--我眼中的单例模式并不完美

/// <summary> /// 全局唯一的配置信息 /// </summary> public class Config { private static Config _config = null; public static Config GetConfig() { ...

架构师修行之路 99

[java设计模式] 我眼中的单例模式(二)

上一篇[java设计模式] 我眼中的单例模式(一) 我们介绍了一些单例(单件)模式的基本概念,本文将对其中介绍的几种实现方式进行介绍: 1. 饿汉模式 类加载的时候就创建实例 优点:类加载的时候就创建一次实例,避免了多线程同步问题 缺点:即使单例没被用到也会创建,浪费内存 public class Singleton{ private static Singleton instance = new Singleton(); private Singleton(){} public static Si

Aran_的博客 227

[java设计模式] 我眼中的单例模式(一)

本篇将对单例模式的定义及应用场景部分进行简要描述 定义: 确保一个类只有一个实例,并且提供一个全局访问点。属于设计模式三大类中的 创建型模式 特点: 只有一个实例 自我实例化 提供全局访问点 优点: 由于单例模式只生成一个实例,所以能够节约资源,减少性能开销,提高系统效率,同时也能够严格控制客户对它的访问。 缺点: 正是因为系统中只有一个实例,这样就导致了单例类的职责过重,违背了“单一职责原则”,同时也没有抽象类,这样扩展起来会有一定困难。 常见的实现方式: 饿汉式 懒汉式 双重检锁式 静态

Aran_的博客 219

Unity精华☀️ 面试官眼中的「设计模式」

前几天跟大家聊了面试时的万向锁解法,那刻在面试官基因里的问题,还有“Unity设计模式”啦~今天就带大家看一下Unity常见的设计模式

星河造梦坊 2544

【Linux】单例模式

某些类,只应该具有一个对象(实例),就称之为单例。 在很多的服务器开发场景中,需要服务器加载很多的数据到内存中,往往需要一个单例的类来管理这些数据 饿汉模式 懒汉模式 懒汉模式的核心思想就是“延时加载“,从而能够优化服务器的启动速度。如果在创建实例对象时不加上synchronized就导致对对象的访问是线程不安全的。 饿汉模式的核心思想就是“立即加载”,饿汉模式是线程安全的,在类创建的同时就会...

Catdeo的博客 799

关于单例模式的一些理解

简介:简述单例模式的几种实现 懒汉式 懒汉的"懒",主要体现在懒加载(延迟加载)上。我们知道,在类加载到JVM的过程中,会先加载静态属性等相关操作,最后一步才是初始化。而类的初始化,是有条件或者说时机的,也就是说,类很有可能是只被加载和连接,而不被初始化的。而所谓的延迟加载,就是让类被初始化时,才去执行创建对象的操作,而不是一开始就创建,从而达到延后加载时机的目的。懒汉式写法如下: //世界上只能...

opt1997的博客 287

浅谈-单例模式

[java]   public class Sta {       public static long date=System.currentTimeMillis();//1       public int da=1;//3,5       static{           System.out.println("Sta:"+System.currentTime

明月阁 1365

一个cpper眼中的singleton

singleton,有人称为单件,还有人叫做单例。它是一个非常简单的设计模式,在GOF的经典著作里面,把它定义为:让一个类只有一个实例,并为实例提供一个全局访问点。 class Singleton { private: Singleton(); ~Singleton(); Singleton(const Singleton & rhs); Singleton & operator=(

tintinr的专栏 882
上一篇: 搭建全分布环境下的habse的错误解决
下一篇: 百度运维部,基础平台研发工程师
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值