我眼中的单例模式

CodeBuddy 接入 MCP,一键生成网站! 2024 年 11 月,Anthropic 公司搞了个挺有意思的新玩意 - Model Context Protocol(模型上下文协议)简称为 MCP 协议。简单来说,它就是给 AI 和各类工具数据之间搭了个标准化的”桥梁”,让开发者不用再为对接问题头疼了。mcp.so如下图所示,这是 mcp.so 网站中的 MCP Server。使用百度/高德地图分析旅线计算时间接 Puppeteer 自动操作网页使用 Github/Gitlab 让大模型接管代码仓库。 阅读详情

单例设计模式

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作为单例来实现。最后,希望大家有所收获。

计算机专业保研攻略(一):个人定位与院校推荐(必看干货) 写在最前 又是一年保研季,作为过来人,我深知保研就是一场信息战。在保研过程中,需要注意的信息太多了,最初我也因为经验不足,走了不少弯路。我写下这篇博客,希望能帮助更多有需要的同学。 怎样做好保研信息分析 我想举几个发生在我身边真实的例子: A同学推免排名不如B同学,但A保研去向比B同学好很多; C同学在年级里成绩并不算突出,但他拿到了最好的offer; D、E同学纷纷圆梦北大、浙大等名校,但是他们的成绩似乎并不突出; F同学取得了目标院校夏令营offer,但没有拿到本校推免资格; G同学认为自己拿不到推免 阅读详情

相关推荐

告别懵圈!用CANoe 5640手把手带你跑通SomeIP TC8测试(附脚本思路)

本文详细解析了基于CANoe 5640的SomeIP TC8测试全流程,从环境搭建到结果分析,特别针对车载以太网测试中的SomeIP协议测试难点提供实战指南。通过典型测试用例SOMEIP_TEST_61,手把手教你配置CANoe工程、编写CAPL脚本,并解决常见问题,助力开发者高效完成协议验证。

weixin_30791095的博客 361

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

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

逆风飞翔的博客 712

DAP-Link DIY复刻指南

DAP-Link是ARM开源的一个项目,主要用于代码下载以及调试,支持SWD和JTAG调试接口,对于开发嵌入式的同学相信都或多或少有所耳闻。除了DAP-Link之外还有很多同类型的调试器,如ST-LinkJ-Link,但是这些都是不开源的,无法复刻或进行二次开发。但DAP-Link不同,完全开源,不用担心侵权问题,且随着开源社区大家的共同努力,DAP-Link经过了不断的迭代,先如今已取代ARM之前推出的CMSIS-DAP开源项目。

爱出名的狗腿子 1万+

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

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

<sdffdsfsdfdfs>sfsfsfsdfsdffds</sdfsDS>Fsd 282

基于FPGA的函数信号发生器设计

设计采用波形查找表和相位累加器的方法实现DDS,查找表的数据位宽为8位,采样点数为4096。波形产生范围是100Hz-20MHz,最小频率间隔为1Hz,用8个数码管显示频率,可产生正弦波、方波、三角波,波形用两个led显示,select信号控制,全灭为正弦波,一亮一灭为方波,全亮为锯齿波。为了使频率和相位便于调节,用3个拨码开关控制步进,用8个led显示状态。

FPGA入门到跑路 5636

手把手教你用STM32单片机控制双向可控硅调光(附完整代码+电路图)

本文提供了一套完整的STM32单片机控制双向可控硅实现灯光无级调光的实战方案。详细讲解了硬件电路设计,包括过零检测与隔离驱动,并剖析了基于定时器中断的软件核心逻辑,通过精准的相位角计算实现平滑的灯光强度控制。附有完整代码与电路图,适合嵌入式开发者实践。

weixin_29194255的博客 957

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

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

一名正义的白帽黑客 1878

渗透测试工具之Sliver

攻击者需要将生成的植入物传输到目标系统,并执行它。C2 框架的架构与 Metasploit(如 Meterpreter)类似,但更专注于。(Red Teaming)中常见的攻击工具之一,攻击者利用 C2 服务器。组成,二者共同实现 C2 服务器与受控设备的通信。植入物(Implants)是 Sliver 生成的。抓包分析时,可以看到所有数据都是加密传输的。和**监听器(Listeners)**功能。Sliver 支持多种通信协议,如。随着安全防御的不断提升,企业应加强。进行通信,数据仍然是加密的。

idlecloud0105的博客 1936

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

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

l13kddd的博客 1745

从微服务到 Agent 服务:架构思维的迁移

微服务的概念最早在2014年由Martin Fowler提出,核心是“将单体应用拆分为一组小的服务,每个服务运行在独立的进程中,服务之间采用轻量级的通信机制互相协作,每个服务围绕具体业务构建,并且可以独立部署到生产环境”。单一职责:每个微服务只负责一个业务领域的功能,比如订单服务只处理订单相关的逻辑,用户服务只处理用户相关的逻辑被动响应:微服务是“哑的”,只有收到外部请求的时候才会执行逻辑,本身没有主动决策的能力固定流程:微服务的逻辑是硬编码的,执行流程是提前定义好的,只能按照程序员写的代码处理请求。

AI智能探索者的博客 275

在 SAP BTP 中通过 MTA 自动创建 Service Broker 的原理与实战

本文探讨了如何在SAP BTP的Cloud Foundry环境中通过MTA部署自动创建和注册Service Broker。传统手动注册方式存在依赖管理员、环境一致性差等问题。通过MTA部署可实现自动化Broker注册,利用占位符生成安全凭证,并确保平台端与应用端认证一致。文章详细解析了关键配置参数,包括create-service-broker标志、Broker名称、认证凭据和访问URL的设置方法。这种基础设施即代码(IaC)方式能提升DevOps实践水平,使服务注册成为应用部署流水线的有机组成部分。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 201

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

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

Dream_Ryoma的博客 616

【FISCO-BCOS】十一、一键部署

一键部署包括以下服务:节点(FISCO-BCOS 2.0+)、管理平台(WeBASE-Web)、节点管理子系统(WeBASE-Node-Manager)、节点前置子系统(WeBASE-Front)、签名服务(WeBASE-Sign)。部署成功后,登录此网站:http://localhost:5000 访问WeBASE管理平台。快速搭建WeBASE管理台环境,方便用户快速体验WeBASE管理平台。修改节点管理子系统和签名服务组系统的mysql数据库配置。如使用root账户:改为。配置方法:参考如下指令。

naihe_fish的博客 2596

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

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

架构师修行之路 1067

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

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

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

miwanmeng的博客 279

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

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

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

架构师小秘圈 1718

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

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

/// <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、付费专栏及课程。

余额充值