Java帝国之单例设计模式

帝国竞争算法(imperialist competitive algorithm, ICA )详解+Java代码实现 前言 这段时间用过这个算法做过相关的工作,今天就介绍一下吧。虽然感觉效果嘛,勉勉强强啦。不过每种算法肯定有其适用的地方,用到了就Mark一下方便后人吧~ 介绍 帝国竞争算法(imperialist competitive algorithm,ICA)是Atashpaz-Gargari和Lucas于2007年提出的一种基于帝国主义殖民竞争机制的进化算法,属于社会启发的随机优化搜索方法。目前,ICA已被成功应用于多种优化问题中,如调度问题、分类问题和机械设计问题等。[2] 帝国主义竞争算法,借鉴了人类历史上政治 阅读详情

前言:本文原作者是王钦誉,原文链接:https://xiaoqinyu0000.github.io/Java/JavaSingleton/

之前王钦誉还写过《Java帝国之拨云见日识回调》。 

这篇文章很长,讲了单例模式的方方面面, 建议认真读完。

1

背景

在Java帝国,有一个隐蔽的村庄叫IO村,村里每个人都身怀绝技。其中,SocketIO、HttpIO、FileIO更是专注于某个领域的高手。

FileIO,它十余年苦练文件存储技术,雄心壮志,决定走出村庄,去外面闯一闯。

FileIO到了城里,成功的通过了面试,进入一家IO科技责任有限公司,专门负责文件存储等工作。

2

懒汉式

FileIO刚进入公司,工作不久,就接到3、4次投诉,投诉理由是“在系统中使用FIleIO之后,频繁地发生内存抖动,导致内存吞吐量骤减”。

FIleIO大急,连忙查看日志,原来在系统运行中,自己的实例被频繁地创建与销毁。

FileIO找到主管老张: “咱们能不能给客户说一下, 让客户对我的实例进行缓存,别这么频繁地创建啊”。

老张,“我们很难去规定客户怎么用,不过我们可以做一些引导。我看你也不需要创建多个实例,你可以创建一个实例给客户使用,不对外开放创建实例的权限就行”。

FileIO眼睛一亮,赶紧请教老张具体该怎么做。

老张说:“其实很容易。

第一、把自己的构造方法设置为private的,不让别人new你的实例;

第二、提供一个static方法给别人获取你的实例,你在这个方法里面返回你自己创建的实例就行”。

FileIO按照老张的思路做了修改:

0?wx_fmt=png

以后别人在调用FIleIO的时候就不再使用new的方式去创建一个FIleIO的实例,而是调用static方法getInstance()获取FileIO的实例,例:

0?wx_fmt=png

这样一来,FileIO就不会频繁的被创建了,

后来,FileIO才知道自己的这种实现机制就是“单例设计模式”,并且被人称为“懒汉式”, 可能是因为在需要的时候才创建吧, 显得很“懒”。

3

饿汉式

FileIO使用单例模式修改后解决了问题,在公司例会上被技术总监大大地表扬了一次。

FileIO非常傲娇,在村里的微信群里向小伙伴们炫耀这件事。

村里的HttpIO自己也存在类似的问题,也需要自修一下,于是它按照了FileIO在微信群中分享的思路进行修改:

0?wx_fmt=png

实现的方式虽有所不同,但调用方式和效果都是一样的(都能实现单例)。

HttpIO总觉得自己的实现方式更好, FileIO则说自己的Lazy方式更流行, 毕竟不调用FileIO的话对象就不会创建。

两人争执不下, 于是就去请教经验丰富的老张。 

老张说,你们这都是单例设计模式的实现方式,HttpIO的实现方式在单例设计模式中被称为饿汉式”(可以能由于太饥饿, 一上来就创建了对象)两者的执行顺序有所不同:

FileIO的实现在第一次调用的时候先执行了getInstance()方法,再执行构造方法。

HttpIO的实现在第一次调用的时候先执行了构造方法,再执行getInstance()方法。

而且,饿汉式是一种线程安全的写法。

4

线程安全(synchronized)

FileIO表示不服, 老张说,我给你举个例子,

当有多个线程并发执行getInstance()的时候,可能会出现以下的情况而导致FileIO产生多个实例。”

线程一 : FileIO.getInstance()

(FileIO:判断fileIO为null,进行fileIO实例的初始化)

线程二: FileIO.getInstance()

(FileIO:  fileIO还没初始化完,依然为null, 于是进行另外一个fileIO实例的初始化)

等到两个线程都返回的时候,其实是创建了两个FileIO的实例。

0?wx_fmt=png

(点击看大图)

FileIO恍然大悟,线程安全可得好好重视,好在以前研究过一点线程安全的问题,直接加上synchronized:

0?wx_fmt=png

这样一来,当有两个线程同时执行getInstance()方法的时候,一旦线程一获取到FileIO.class锁,线程二只能在外面等待着。

在线程一执行完getInstance()的逻辑后释放FileIO.class锁,其他线程才能获取这个锁进入getInstance()方法中。

这样就避免了创建两个FileIO的实例

流程如下:

0?wx_fmt=png

(点击看大图)

5

线程安全-双重检验锁

老张锊一锊胡须,道:“这种做法简单明了,确实保证了线程安全, 还能更优化呢!”

FileIO不服气,“我不信,难道还有比我只加一个synchronized更简单的做法?”。

老张笑了一下,“我说的优化并不是指简单化。你想想啊,如果fileIO实例不为空时,还需要使用synchronized来限制执行时只能一个线程进入吗?来来来,老哥给你露一手。”

0?wx_fmt=png

“我这招江湖人称”双重检验锁“,够酷吧!”。

“老司机就是复杂,好端端的,被你弄了那么多个fileIO == null的判断,还用volatile关键字修饰fileIO,这样真的能提升性能吗?”

“you look,当有多有线程调用getInstance()方法的时候,不管三七二十一,先让他们进来。如果fileIO实例不为空,那最好了,直接return实例fileIO,跟synchronized一点都扯不上关系,所以也不会影响到性能。这是双重检验中的第一次检验。”

“oh,I know,如果fileIO是null的,就进入synchronized语句块,在synchronized语句块里面初始化对象。但为什么在synchronized语句中需要再次检查fileIO实例是否为null?”

“这就是第二次检验了,当有多个线程通过第一次检验时,假设线程拿到锁进入synchronized语句块,对fileIO实例进行初始化,释放FileIO.class锁之后,线程二持有这个锁进入synchronized语句块,此时又对fileIO对象就行初始化。所以在这里进行第二次检验防止这种意外发生。”

“我理解了,但我不明白fileIO为什么要用volatile关键字修饰?”

“我们假设线程一进入第二次检验之后就执行FileIO fileIO = new FIleIO()操作,在这个操作中,JVM主要干了三件事

1、在堆空间里分配一部分空间;

2、执行FileIO的构造方法进行初始化;

3、把fileIO对象指向在堆空间里分配好的空间。

但是,当我们编译的时候,编译器在生成汇编代码的时候会对流程顺序进行优化。优化的结果是有可能按照1-2-3顺序执行,也可能按照1-3-2顺序执行。

我们知道,执行完3的时候就fileIO对象就已经不为空了,如果是按照1-3-2的顺序执行,恰巧在执行到3的时候(还没执行2),突然跑来了一个线程,进来getInstance()方法之后判断fileIO不为空就返回了fileIO实例。

此时fileIO实例虽不为空,但它还没执行构造方法进行初始化。又恰巧构造方法里面需要对某些参数进行初始化。后来闯进来的线程糊里糊涂对那些需要初始化的参数进行操作就有可能报错奔溃了。”

6

线程安全-静态内部类

“弄一个单例模式要这么麻烦,写这么多代码,我还是使用饿汉式单例模式算了!”,FileIO抱怨说。

老张笑着说,“那也不能这么想呀,其实我们可以根据使用场景不同来使用不一样的单例模式。如果我们需要在getInstance()方法的时候传入参数进来辅助构造方法初始化,那就得用懒汉式了,比如:“

0?wx_fmt=png

FileIO一想,也对,其他情况就可以使用饿汉式了。

老张好像看出FileIO的想法,道:”其实饿汉式也有其他弊端,比如当我们不想获取FileIO的实例而是想获取其中一个全局变量的时候,在类加载的时候还是会对fileIO实例进行初始化,导致时间比较久。举例如下:

0?wx_fmt=png

当调用FileIO.TYPE_MP3的时候,INSTANCE实例也会被初始化,这显然不是我们需要的。所以,我们Java帝国的高手们又想出了一种叫静态内部类的单例模式,它简单又保证实例能进行懒加载。”

0?wx_fmt=png

FileIO眼睛一亮,“这个我能理解,当执行getInstance()方法的时候就去调用FileIOHolder内部类里面的INSTANCE实例,此时FileIOHolder内部类会被加载到内存里,在类加载的时候就对INSTANCE实例进行初始化。和饿汉式一个道理,保证了只有一个实例,而且在调用getInstance()方法的时候才进行INSTANCE实例的初始化,又具有懒汉式的部分特性。”

老张满意地说,“是的,但这种写法是利用JVM的机制完成的,在其他语言不一样适用哦!”

7

黑客破坏-反射和反序列化

听了老张一堂课,FileIO收获匪浅,这些都是以前在村里老村长没讲过的一些技巧,它非常高兴,仿佛看到自己在以后踏上人生巅峰、迎娶白富美的样子。

老张决定给它泼泼冷水,“还没完呢,在我们Java帝国,有很多被称为黑客的家伙,他们经常搞破坏,你如果只按照上面介绍的写法使用单利模式,很有可能被破坏哦!”

FileIO愣住了,接着问,“那怎么办呀,他们都是怎么破坏的?”。

老张故作深沉地答,”其实他们的破坏方式无非就是这两种:

1. 反射调用构造方法初始化新的实例

FileIO fileIO = FileIO.class.newInstance();

2. 序列化和反序列化产生新的实例

0?wx_fmt=png

对于通过反射调用构造方法的破坏方式我们可以通过在增加全局变量flag,在第一次初始化的时候就设置为true,第二次初始化的时候判断到flag为true就抛出异常。但这种办法也只能避免破坏,无法彻底阻止,因为他们可以反射flag来修改flag的值。

对于使用序列化和反序列化产生新的实例的方式就容易避免了,可以增加readResolve()方法来预防。我们使用静态内部类的方式来演示如何避免:”

0?wx_fmt=png

FileIO不解:”为什么增加readResolve()方法并在里面返回之前创建好的实例就可以避免被反序列破坏呢?“

“这是反序列化机制决定的, 在反序列化的时候会判断如果实现了serializable 或者 externalizable接口的类中又包含readResolve()方法的话,会直接调用readResolve()方法来获取实例。”,老张解释道。

8

终极招数

FileIO叹一叹气,”虽然单例看起来简单, 但是要弄一个完美的单例模式还是比较麻烦的!“

老张眨了眨眼,“我还有终极招数呢!”

“咦!”

0?wx_fmt=png

FileIO看着简单又陌生的自己,向老张投去疑惑又质疑的眼神。

老张解释:“别看你现在是枚举类型,但实际上反编译可知枚举实际上就是一个继承Enum的类。所以你的本质还是一个类,因为枚举的特点,你只会有一个实例,同时保证了线程安全、反射安全和反序列化安全。”

FileIO感慨,原来单例也可以这么简单,“你妹的老张,有这么简单的方法你不早告诉我。”

“我们重在过程,不在结果。”

(完)

你看到的只是冰山一角, 更多精彩文章,参见《码农翻身2016文章精华》

有心得想和大家分享? 欢迎投稿 ! 我的联系方式:微信:liuxinlehan  QQ: 3340792577

0?wx_fmt=jpeg

公众号:码农翻身

“码农翻身”公众号由工作15年的前IBM架构师创建,分享编程和职场的经验教训。

C#实战:基于ONNX与SCRFD/ArcFace的高效人脸识别系统开发 本文详细介绍了如何在C#环境中,利用ONNX Runtime高效部署SCRFD人脸检测与ArcFace人脸识别模型,构建一个完整的、高性能的人脸识别系统。内容涵盖环境搭建、模型推理、人脸对齐、特征提取等核心模块,并提供了从Demo到生产系统的优化思路与实战代码,为.NET开发者提供了一套开箱即用的解决方案。 阅读详情

相关推荐

【GPT】Coze使用开放平台接口-【7】Dify 比较篇

coze 用于社交,dify 用来内部构建。抛开工作流,机器人,工具,coze 最大的区别在于可以直接发布到社交媒体上。所以,coze 对操作体验上也花了不少功夫,而 dify 就专注在帮助使用者打磨工作流。coze 各个大模块之前可以联动的机制也比较多,所以侧重点各不相同。

非晓为骁的博客 7615

设计模式之——单例模式

更酷的是,创建对象的职责和管理单例的职责被拆分在两个不同的方法中,这两个方法组合起来才具有单例模式的威力。你学到了吗,少年?

很高兴你能看到这里 3065

BCM-BCP-DRP-运维管理之故障管理——故障的分类与处理流程

ITSM Business Continuity Manage(BCM) BusinessContinuityPlan(BCP) 偏业务 DisasterRecoveryPlan(DRP) 偏IT 天塌了,考虑在天塌了的状态下做什么,怎么开展业务就是BCP; 天塌了,考虑怎么把天撑回去,就是DRP。 DRP+BCP+供应链管理+危机管理等就是BCM (业务连续性管...

mmgithub123的博客 2725

Java设计模式-单例模式

创建者模式概述 创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。 这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。 创建型模式分为: 单例模式 工厂方法模式 抽象工程模式 原型模式 建造者模式 1.单例模式概述 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种

Java硬件工程师的博客 2401

箱式连续提升机

箱式连续提升机,是对具有高度差较大(>1500mm)的输送线之间进行连接,将箱式物资进行连续输送的提升(或下降)的输送设备,可根据输送线的具体输送要求作相应的参数设计。目前的箱式连续提升机种类比较多,常用的形式主要有Z型和C型,工作原理相同,只是入口和出口分别在两侧和同侧。

月生沧溟 1252

【系统分析师】高分论文:论软件开发方法及应用(数据中心CMDB 系统)

【摘要】本文以作者负责的"数据中心CMDB系统"建设项目为例,探讨了系统分析和建模方法在IT项目中的应用实践。项目基于ITIL框架,旨在整合分散的配置信息,构建统一CMDB平台。通过分析结构化、面向对象和构件化三种开发方法特点,项目最终采用结构化分析与V模型结合的方式,并融入面向对象设计理念。系统经过4个月开发后上线,通过增量迭代满足不同数据源接口需求,采用适配器模式提高兼容性。经过2个月试点运行和优化,系统已稳定运行并获得用户认可,为后续IT服务管理集成奠定了基础。项目实施使作者积累了

数据知道的博客 4300

一文带你彻底搞懂设计模式之单例模式!!由浅入深,图文并茂,超超超详细的单例模式讲解!!

本篇文章通过图文形式,由浅入深,详细讲解了设计模式中的单例模式的应用

祝你身体健康,美满幸福 1万+

一网打尽身份验证与授权:深入剖析ASP.NET Core Identity在C#中的全方位实战应用

ASP.NET Core Identity 是 Microsoft 提供的一个强大且可扩展的身份验证和授权框架,用于构建基于用户账户、角色和权限的Web应用程序。它提供了用户注册、登录、密码管理、多因素认证、角色分配、授权等功能,与ASP.NET Core框架深度集成,简化了安全相关的开发工作。通过上述代码示例和注释,展示了如何安装配置Identity、自定义用户实体与数据库上下文、实现用户注册登录、管理角色与用户角色关系,以及如何使用授权策略保护应用程序资源。,并指定用户和角色类型以及主键类型。

java专栏 1530

单例设计模式(8种)

/* * 单例设计模式: * 1.所谓的单例设计模式,就是采用一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。 * * 2.如何实现? * 饿汉式 vs 懒汉式 * 3.区分饿汉式和懒汉式 * 饿汉式:(创建时就已经存在对象) * 坏处:对象加载时间过长。 * 好处:饿汉式是线程安全的。 * 懒汉式: * 好处:延迟对象的创建 * 目前写法的坏处:线程不安全。---->到多线程内容时,在修改 * */ public class Si.

weixin_49329785的博客 1707

行为预测模型:Social LSTM与Graph Neural Networks

行为预测模型:Social LSTM与Graph Neural Networks ,人工智能,计算机视觉,大模型,AI,本文围绕行为预测模型Social LSTM与Graph Neural Networks展开,介绍了两者的基础概念、原理、应用场景,还对比了它们在优势、劣势和适用场景上的不同,最后给出了相应的代码示例。Social LSTM基于LSTM,通过社交池化层处理多智能体交互;图神经网络处理图结构数据,靠消息传递获取信息,二者各有侧重,适用于不同场景。

不踩坑,不知道坑有多深;不总结,踩过的坑白踩。 15万+

【教程4>第4章>第12节】通过FPGA实现基于FFT变换的频偏估计和补偿

基于FFT变换的频偏估计和补偿,其基本原理如下:而频偏补偿的目的是将频偏后的信号频谱移回原来的位置。

FPGA/MATLAB学习教程/源码/项目合作开发 882

单例模式(单例设计模式)详解

在有些系统中,为了节省内存资源、保证数据内容的一致性,对某些类要求只能创建一个实例,这就是所谓的单例模式。 单例模式的定义与特点 单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。 在计算机系统中,还有 Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、打印机的后台处理服务、应用程

GJ_007的博客 4万+

SAP CAP core data service 里 association 和 composition 的区别

我们进行 SAP CAP 开发时,在schema.cds里进行数据建模,可以使用 association 和 composition,来描述两个 entity 之间的关联关系。在 SAP BTP 的 Cloud Application Programming Model (CAP) 中,cds文件定义了数据模型,而和是用于建立实体关系的两种关键机制。理解它们的区别对于构建高效、清晰的 CAP 数据模型至关重要。下面,我们将通过严谨的逻辑推理,结合代码示例,深入分析和之间的核心差异。

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

STM32F103C8T6驱动DHT11温湿度传感器:从CubeMX到稳定驱动全解析

在嵌入式开发中,传感器数据采集是单片机最常见的应用场景之一。无论是环境监测还是智能家居,MCU都需要通过可靠的通信协议读取外部传感器的数据。单总线协议因其占用引脚少、实现简单,特别适合DHT11这类低速温湿度传感器。然而,实际开发中常遇到时序抖动、驱动不稳、延时不准等问题。本文以经典组合STM32F1系列F103C8T6与DHT11为切入点,从选型、CubeMX工程配置、GPIO开漏输出设计,到基于DWT的微秒延时与完整驱动代码,系统梳理了单总线协议的实践要点。通过合理的读取间隔、低功耗唤醒与数据滤波,可以

weixin_33859231的博客 387

VC中sqlite的使用

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

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

APB Watchdog UVM验证实战:从协议合规到寄存器模型调优

APB Watchdog是一种典型低复杂度但高验证价值的SoC基础IP,其核心涉及APB总线协议、寄存器映射与状态机行为三重验证维度。理解APB时序约束(如PREADY响应窗口)是功能正确的前提;掌握UVM寄存器模型中镜像值与硬件异步行为的同步机制(如uvm_reg_cbs回调),可解决VALUE寄存器清零后值不一致等高频问题;而结合functional coverage驱动的testcase设计,则保障timeout、interrupt、reset等关键路径全覆盖。该模块广泛应用于汽车MCU、IoT So

weixin_34320724的博客 513

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Java 帝国对 Python 的渗透能成功吗?

作者 |刘欣 责编 | 屠敏 本文经授权转载自码农翻身(ID:coderising) 5G之后,中国的人工智能发展趋势如何? https://edu.youkuaiyun.com/topic/ai30?utm_source=csdn_bw 引子 Java 帝国已经成立20多年,经过历代国王的励精图治,可以说是地大物博,码农众多。 可是国王依然不满足,整天想着如何...

优快云资讯 5829

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

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

邪恶的Java帝国是怎么欺负小函数的?

1小函数的运气不好,投胎到了邪恶的Java帝国,一出生就被告知了自己的悲惨地位,以及未来的悲惨人生:奴隶。确切地说,是类的奴隶。在Java帝国, 国王特别喜欢“类”, 不...

码农翻身 2134

Java : 一个帝国的诞生

Java基础 idea安装教程 java

sunyingboaini的博客 450

神秘使者到Java帝国传道协程,竟被轰了出去

作者|轩辕之风O来源 |编程技术宇宙(ID:xuanyuancoding)头图 | 优快云下载自东方IC神秘使者“久闻Java语言跨越平台,框架众多,不过二十年功夫,就已晋升...

优快云资讯 3098

java帝国的崛起

1 C语言帝国的统治 现在是公元1995年, C语言帝国已经统治了我们20多年, 实在是太久了。  1972年, 随着C语言的诞生和Unix的问世, 帝国迅速建立统治, 从北美到欧洲, 从欧洲到亚洲,  无数程序员臣服在他的脚下。 帝国给我们提供了极好的福利:贴近硬件,  运行极快, 效率极高。   使用这些福利...

neverstopagain的博客 165

黑客帝国里的代码雨-java代码实现

       我一直很好奇黑客帝国里的代码雨,感觉很酷,就一直搜集资料,尝试使用Java来实现,终于成功了,分享一下啊       代码雨的实现用到的是jdk自身带的类库,可能很多小伙伴不知道Java安装JDK就可以做C/S(客户/服务器)桌面应用软件开发,现在使用较多的是B/S(浏览器/服务器)模式。好了闲话少叙,只要你的的电脑支持桌面显示(Linux服务器纯命令行除外),那么ni只需要安装好...

m0_37732829的博客 1万+

学习java之java帝国的诞生

Java : 一个帝国的诞生 C语言帝国的统治 现在是公元1995年, C语言帝国已经统治了我们20多年, 实在是太久了。 1972年, 随着C语言的诞生和Unix的问世, 帝国迅速建立统治, 从北美到欧洲, 从欧洲到亚洲, 无数程序员臣服在他的脚下。 帝国给我们提供了极好的福利:贴近硬件, 运行极快, 效率极高。 使用这些福利, 程序员们用C 开发了很多系统级软件,操作系统, 编译器, 数据库,网络系统… 但是帝国也给我们安上了两个沉重的枷锁: 指针和内存管理 虽然指针无比强大, 能直接操作内存,

Fcglywaj的博客 266
上一篇: Java 帝国之消息队列
下一篇: Java帝国之JMS的诞生
码农翻身
码农翻身 《码农翻身》作者 《码农翻身》作者
博客等级 码龄8年 3万+粉丝 1084原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值