KandQ:单例模式的七种写法及其相关问题解析

SpringBoot+Vue+MySQL 高校学科竞赛平台 完整可运行版(源码+文档) 本项目基于SpringBoot+Vue+MySQL构建高校学科竞赛管理平台,覆盖学生、教师、领队教师、管理员四大角色,实现竞赛发布、报名、刷题、评分、公示等全流程闭环管理。系统架构清晰,技术主流,配套完整源码、数据库脚本、万字论文、开题报告、答辩PPT及部署文档,支持一键运行,适用于毕业设计、课程设计及院系二次开发,真正实现“开箱即用”。 阅读详情

@改进自:http://cantellow.iteye.com/blog/838473

设计模式中的单例模式可以有7种写法,这7种写法有各自的优点和缺点。

代码示例(java)及其分析如下:

一、懒汉式

public class Singleton

{

private static Singleton singleton;

private Singleton(){}

public static Singleton getInstance()

{

if(singleton==null)

singleton=new Singleton();

return singleton;

}

}

}

优点:不是马上就初始化的,当需要使用的时候才进行初始化(即是lazy loading)

缺点:在并发情况下是线程不安全的

二、懒汉式线程安全版

public class Singleton

{
private static Singleton singleton;

private Singleton(){}

public synchronized static Singleton getInstance()

{

if(singleton==null)

singleton=new Singleton();

return singleton;

}
}

 

优点:不是类加载之后就进行初始化的,当需要使用的时候才进行初始化(即是lazy loading),且为线程安全的

缺点:效率低,加了synchronized进行同步之后,效率上有所降低

三、饿汉式

public class Singleton

{

private static Singleton singleton=new Singleton();

private Singleton(){}

public static Singleton getInstance()

{

return singleton;

}

}

 

这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法,但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。其一个明显的好处就是是线程安全的

四、饿汉式的变种写法

public class Singleton

{

private static Singleton singleton;

static

{

singleton=new Singleton();

}

private Singleton(){}

public static Singleton getInstance()

{

return singleton;

}

}

其会在类加载的时候就进行加载。和上面的饿汉式的写法优缺点相同

五、静态内部类方式

public class Singleton

{

private Singleton(){}

private static class SingletonHolder

{

private static final Singleton singleton=new Singleton();

}

public static Singleton getInstance()

{

return SingletonHolder.singleton;

}

}

这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟第三种和第四种方式不同的是(很细微的差别): 第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方式就显得很合理。

六、采用枚举方式

public enum Singletons

{

INSTANCE;

//此处表示单例对象里面的各种方法

public void Method()

{

}

}

Effective Java作者Josh Bloch提倡使用枚举的方式去实现单例模式。因为它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,同时写法简单。对于枚举方式创建单例,为何可以避免多线程的同步以及防止反序列化重新创建新的对象这个原因,详见相关博文:枚举的线程安全性及序列化问题

七、双重校验锁

public class Singleton

{

private volatile static Singleton singleton;

private Singleton(){}

public static Singleton getInstance()

{

if (singleton == null)

{

synchronized (Singleton.class)

{

if (singleton == null)

{

singleton = new Singleton();

}

}

}

return singleton;

}

}

对于双重校验锁,其是对懒汉式线程安全版的改进,其目的在于减少同步所用的开销。对singleton进行两次判null检查,一次是在同步块外,一次是在同步块内。为什么在同步块内还要再检验一次?因为可能会有多个线程一起进入同步块外的 if,如果在同步块内不进行二次检验的话就会生成多个实例。

对singleton变量使用volatile关键字的原因是,instance = new Singleton()这句,并非是一个原子操作,事实上在 JVM中这句话大概做了下面 3件事情:
给 instance 分配内存
调用 Singleton 的构造函数来初始化成员变量
将instance对象指向分配的内存空间(执行完这步instance 就为非null 了)
但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是1-2-3 也可能是1-3-2。如果是后者,则在3 执行完毕、2未执行之前,被线程二抢占了,这时 instance已经是非 null了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地jvm就会报错。有些人认为使用volatile 的原因是可见性,也就是可以保证线程在本地不会存有instance 的副本,每次都是去主内存中读取。但其实是不对的。使用volatile 的主要原因是其另一个特性:禁止指令重排序优化。也就是说,在volatile 变量的赋值操作后面会有一个内存屏障(生成的汇编代码上),读操作不会被重排序到内存屏障之前。比如上面的例子,取操作必须在执行完1-2-3 之后或者1-3-2 之后,不存在执行到1-3 然后取到值的情况。从「先行发生原则」的角度理解的话,就是对于一个volatile 变量的写操作都先行发生于后面对这个变量的读操作(这里的“后面”是时间上的先后顺序)。

对于第一种和第二种写法,实际上其可以归类为懒汉式这一种写法,对于第三种和第四种,其也可以归为饿汉式这一种写法。为此,一般单例都是五种写法。懒汉,饿汉,双重校验锁,枚举和静态内部类

对于单例模式,其有两个问题需要注意:

1.如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。

2.如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。

对第一个问题修复的办法是:

private static Class getClass(String classname)throws ClassNotFoundException

{

//获取当前执行线程的上下文类加载器

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

if (classLoader == null)

classLoader = Singleton.class.getClassLoader();

return (classLoader.loadClass(classname));

}

对第二个问题修复的办法是:

class Singletones implements java.io.Serializable

{     

   private static Singletones INSTANCE = new Singletones();  

   private Singletones()

   {          

   }

   public static  Singletones getInstance()

   {

   return INSTANCE;

   }

/*

我们反序列化后获得的并不是原来的对象,而是经过重构的新的对象实例。

    ObjectInputStream对象在反序列化的时候,会在从I/O流中读取对象时,调用readResolve()方法。实际上就是用readResolve()中返回的对象直接替换在反序列化过程中重构的对象。

*/

   private Object readResolve()

   {

        return Singletones.getInstance();     

   }

}   

原因详见博文:Serializable或者Externalizable对象的序列化

电流互感器的使用分析 有关电流互感器使用的注意事项,回头再看,耳目一新。 阅读详情

相关推荐

基于Ansys Fluent 的颗粒分离/过滤解决方案

过滤是指通过特殊装置将颗粒移除,将流体提纯净化的过程。过滤的方式很多,应用的物系也很广泛,固-液、固-气、大颗粒-小颗粒等。

sh_djoin的博客 1852

KandQ:单例模式的七种写法及其相关问题解析

设计模式中的单例模式可以有7种写法,这7种写法有各自的优点和缺点: 代码示例(java)及其分析如下: 一、懒汉式 public class Singleton { private static Singleton singleton; private Singleton() { } public static Singleton getIns...

diaosangwan0363的博客 145

Anaconda下安装LabelImg

Anaconda下安装LabelImg

LYH8543的博客 757

KandQ:那年,那树,那些知识点

写在前面:  对于数据结构的学习,注定绕不开“树”这一起着重要作用的数据结构。“树”在整个数据结构的学习过程中具有举足轻重的地位,而与“树”相关的知识点,往往较为晦涩难懂且容易混淆、忘记。为此,打算根据自己的所学与所思,总结出较为常用的树的相关知识点,同时还记录了自动机的相关的知识,一方面用作自己的备忘录,另一方面,用作分享。为此,如有不对的地方,希望各位能够不吝指出。 ps:点击相关的...

diaosangwan0363的博客 168

Python开发者必看:5分钟搞定Gemini API的3种调用方式(含代码示例)

本文为Python开发者详细介绍了三种高效调用Gemini API的核心方式:基础同步调用、流式调用和聊天模式,并涵盖多模态应用等高级特性。通过清晰的代码示例和实战场景分析,帮助开发者快速掌握Gemini API的集成与优化,提升AI应用开发效率。

g8f9d0s1a2的博客 1123

西威SIEI变频器与电梯主板及驱动板原理图解析与详解(共二十九页原厂PDF图纸)

通过本文的分析,可以了解到西威变频器的技术特点、维修需求以及相关的图纸获取途径。在今后的工作中,维修人员需要充分了解设备的电路图和原理图,掌握正确的维修技术和方法,确保设备的正常运行和延长使用寿命。对于维修人员来说,获取准确的变频器图纸是进行维修工作的基础。这些图纸包含了变频器的电路图、原理图以及相关的元件布局等信息,是维修人员了解设备工作原理、确定维修步骤的重要依据。西威变频器是一种高效、可靠的电机控制设备,广泛应用于电梯、空调、风机等领域的电机控制中。目前,西威提供的维修图纸主要以PDF格式为主。

lXSqtACEzg的博客 1115

K:枚举的线程安全性及其序列化问题

@改进自:http://blog.youkuaiyun.com/jinzhencs/article/details/50561359 枚举是如何保证线程安全的且其在序列化和反序列化的操作中是单例的 要想看源码,首先得有一个类吧,那么枚举类型到底是什么类呢?是enum吗?答案很明显不是,enum就和class一样,只是一个关键字,他并不是一个类,那么枚举是由什么类维护的呢,我们简单的写一个枚举: publ

MyStringIsNotNull的博客 782

SAR ADC设计——SAR Logic原理

SAR ADC设计——SAR Logic原理 参考:集成电路设计实践 SAR ADC 清华大学 李福乐 文章目录SAR ADC设计——SAR Logic原理一. Overview二. TSPC(True Single Phase Clock)三. SC_Gen(Sequential Control Generator)四. LATCH_Dynamic五. 时序控制逻辑总结六. SW产生七. DO产生八. GT产生九. SAR Logic模块总结 一. Overview 输入:CKC,CompB,SAMPL

夏风喃喃的博客 1万+

Q:记学习枚举过程中的一个小问题

在学习有关java枚举的时候,我们知道了所有的枚举类型均是继承自java.lang.Enum类的,且所有的枚举常量均是该枚举类型的一个对象,且对象名即为该枚举常量的名称。 例子如下: 源码: public enum T {     SPRING,SUMMER,AUTUMN,WINTER; }   反编译后的代码: public final class T extends Enum

MyStringIsNotNull的博客 385

嵌入式开发 DSP芯片运行方式(挖坑-填坑中-坑已平 :))

TMS320F28335 DSP 芯片 如何将运行代码放在不同的位置

FOOLCODE的专栏 1306

R语言 Chow Test

Chow 测试用于测试不同数据集上的两个不同回归模型中的系数是否相等。 该测试通常用于计量经济学领域,使用时间序列数据来确定数据在某个时间点是否存在结构性中断。 本教程提供了如何在 R 中执行 Chow 测试的分步示例 首先,我们将创建一些假数据: #create data data <- data.frame(x = c(1, 1, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 9, 10, 10, 11, 12, 12, 13,

Mrrunsen的博客 1946

Q:java中关于String类型的一个问题

在java中,曾经编写过一个代码,其导致了程序运行的出错,经过多次检查之后,发现其问题出现在字符串对象上。其问题代码简写如下: class Test {     private String questionString=null;     public static void main(String[] args)     {         Test as=new Test();

MyStringIsNotNull的博客 310

ADS基础教程24 - Gerber文件的导入

一、引言二、基本概念1.仿真文件下载2.仿真文件介绍二、导入步骤1.新建workspace2.选择Layout结构3.导入设计4.选择文件类型5.导入文件6.预览文件内容7.铜皮离散问题四、总结选择。通过本文章结束,可以了解ADS中如何将导入Gerber文件,并且对导入的铜皮进行合并。下一步就针对导入的文件进行层叠设置和编辑。END!}}END!

XZ_ZC的博客 3057

InnoDB 内存结构总览:Change Buffer、AHI 与 Log Buffer

本文系统梳理InnoDB内存结构中的ChangeBuffer、Adaptive Hash Index(AHI)与LogBuffer。ChangeBuffer缓存二级索引页不在内存时的变更,减少随机IO,仅在页加载时合并;聚簇索引修改立即执行,因数据本身不可延后。AHI基于高频等值查询自动构建哈希索引,加速定位,仅支持等值操作。LogBuffer为redo log内存缓冲,配合磁盘日志实现崩溃恢复。三者均服务于性能优化,其中ChangeBuffer与AHI共享BufferPool空间,而LogBuffer独立

bksczm的博客 296

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

信创环境下PageOffice文档控件适配与实践

信创适配做到文档控件这一层,拼的已经不是"有没有国产版本",而是同一个功能在两类终端上是否真的等价。等价的标准很具体:同一次点击能得到同样的结果,同一份公文能得到同样的版式,同一个项目不需要维护两套代码。这背后是三件并不轻松的工作:服务端用一套接口同时支撑两类终端,客户端把两种系统与四种芯片的安装包分发清楚,文档内核层由产品侧跟着 WPS 的版本走。浏览器这一层的压力,则被收进了产品自己的窗口里——升级节奏由产品掌握,不再交给运气。

zi_wu_xian的专栏 326

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

第三篇 第 6 课

前置:第三篇第 1 课(stdx 引入),第二篇第 1 课(std.regex)。环境:仓颉 SDK 1.2.0。本文所有代码与输出均逐行实测。

weixin_41618057的博客 514

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

V8 机制、手写核心函数与 TypeScript 类型体操

或固定初始化(如数组先定长、对象先给全字段)保持类型与形状稳定;这些都比"换框架"更基础。只能处理纯数据(丢函数、Date、RegExp、循环引用报错)。生产级深拷贝要处理:原始值短路、循环引用、V8(Chrome / Node 的 JS 引擎)不是"解释一行跑一行"那么简单,它用。图:V8 执行流程——解释执行 + 热点优化,优化失败会 Deopt 回退。JS 内存自动管理,但"自动"不等于"不会漏"。是"固定间隔最多执行一次"。手写 Promise 是"懂异步"的试金石。是"停下后才执行最后一次",

qq_38468358的博客 351

【从零理解 Spring AI · 12】MCP 的两种角色:做客户端还是做服务端

上一篇讲了 MCP 的概念:标准协议、三大角色、M+N 替代 M×N。

nice_lcj520的博客 533

Java 集合容器高频面试题全解:HashMap 树化阈值、1.7 死循环与 ConcurrentHashMap 演进(2026 招聘 JD 真题复盘)

HashMap 树化阈值 8 的泊松分布依据、退化阈值 6 的防抖设计、JDK 1.7 头插成环死循环的排查与修复、1.8 尾插后为何仍线程不安全、ConcurrentHashMap 放弃分段锁的原因与 ForwardingNode 协作扩容、computeIfAbsent 死锁陷阱、大集合找重复的场景题选型,附三层追问链自测。题目来源为 38 期公开招聘 JD 样本与字节、拼多多、建行、美团、快手公开面经。

蒙眼过河 418

spdlog 同步日志库使用详解

spdlog是一个轻量级 C++ 日志库,支持同步日志、异步日志、多线程日志、日志文件轮转和自定义日志格式。对于嵌入式 Linux 应用开发,我建议先使用。它可以帮我们完成:日志级别过滤。时间戳和线程 ID 输出。日志格式化。终端和文件输出。多线程安全写入。日志文件按大小或时间轮转。我们不需要自己实现互斥锁、文件写入、日志格式化和轮转等底层功能。

zengjiajun2006的博客 1118

JavaSE通关手册:数据类型与变量

代码无需额外处理,编译时编译器自动完成转换。

xys_678的博客 209

Java 多态 学习笔记

多态是面向对象编程的重要特性,指同一操作作用于不同对象时产生不同行为。其核心在于“父类引用指向子类对象”,结合方法重写实现运行时动态绑定。多态使代码更灵活、可扩展,如用Person p = new Student();统一处理各类人员注册,无需修改调用逻辑。但多态无法直接调用子类特有方法,需通过instanceof判断并强制转换。编译时看左边(类型),运行时看右边(实际对象),实现解耦与维护便利。

AA_AA114514的博客 367

IDEA 简介:从零认识 IntelliJ IDEA

IDEA 全称,是由 JetBrains 公司开发的一款主流 Java 集成开发环境(IDE)。它集代码编辑、编译、调试、测试、版本控制、构建管理等功能于一体,被大量 Java 开发者视为日常开发的首选工具。所谓 IDE(Integrated Development Environment,集成开发环境),就是把写代码所需的各类工具整合到同一个界面中。相比只用文本编辑器加命令行编译,IDE 能显著减少重复操作,让开发者把精力集中在业务逻辑上。

ztI10的博客 173

Java实现批量线段相交检测(跨积判定+扫描线优化与踩坑复盘)

【代码】Java实现批量线段相交检测(跨积判定+扫描线优化与踩坑复盘)

m0_64476639的博客 339
上一篇: K:Serializable或Externalizable对象的序列化
下一篇: Q:记学习枚举过程中的一个小问题
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值