设计模式-单例模式详解

Dynamics 365 On-Premise Server v9.0部署体验 期盼已久的Dynamics 365 On-Premise的版本升级终于在11月初姗姗来迟。 11月2日起已经可以在官方渠道下载正式版的Dynamics 365 Server v9.0安装文件,版本号9.0.2,随手附上中文版安装文件下载地址。 想必已经有不少小伙伴开始尝试各种升级、安装了吧。 Dynamics 365 On-Premise的安装过程,度娘一下已经有很多了,这里就不赘述了,只是... 阅读详情
单例模式
单例模式的定义

在整个程序的生命周期中,有且只有一个这样的实例,并且保证是线程安全的。

单例模式的应用场景

例如Spring中的ApplicationContext,还有数据库的连接池都属于单例的模式,他们只会存在一个实例。

单例模式的几种写法
饿汉式单例

我们先来看写法

public class HungrySingleton{
	private final HungrySingleton instance = new HungrySingleton();
    private HungrySingleton(){}
    public static HungrySingleton getInstance{
        return instance;
    }
}

饿汉式的单例写法,就是在一开始就创建好了对象,并且调用getInstance时候直接将对象返回,这也是最简单的单例写法。

首先我想说的是,由于在JVM层面,这个对象就已经创建完成,所以不存在线程安全问题但是,如果这种单例的写法多了,那么就会存在明明我没有使用这个类却还是被JVM分配内存的情况,造成不必要的资源浪费。

不过,如果系统中这种单例写法比较少的话,还是挺方便的不是吗。

懒汉式单例

相比饿汉单例的写法,其实对于内存浪费这一块问题的优化,由于懒汉遵循,你如果不使用我,那么我就不初始化的原则,所以对于饿汉单例的写法要相对好一些。

这里要说明的一点,懒汉式其实也有不下两种的写法,我这里就直接使用静态内部类的方法来代表懒汉式单例。这样是比较优的一种写法。

public class LazySingleton{
    public static LazySingleton getInstance(){
        return LazySingletonHolder.LazySingleton;
    }
    private LazySingleton(){}
    private static class LazySingletonHolder{
        public static final LazySingleton = new LazySingleton();
    }
}

为什么说这种方法会优于其他的懒汉式单例呢,这个我们从类的加载顺序开始讲解,首先,当LazySingleton被JVM装载的时候,第一步会去装载LazySingletonHolder这个静态的内部类,而由于存在于内部类中的LazySingleton未被用于逻辑处理,所以暂时不会被初始化,当外部调用了getInstance的时候,才会被初始化。这样就避免了内存浪费的问题。

这种形式避免了内存的浪费,又因为内部类一定会在方法调用之前初始化,所以巧妙的避免了线程安全问题。

所以懒汉式的单例模式,我比较推荐这种,当然为了让看客能够理解,这种的好处,我就简单的写出不使用这种方法会有什么隐患。

那么第一种最简单的写法

public class LazySingleton{
    private static lazySingleton instance = null;
    private lazySingleton(){}
    public static LazySingleton getInstance(){
        instance = new LazySingleton();
        return instance;
    }
}

大家可以很明显看出,这种单例模式虽然符合你需要我的时候,我才初始化的定义,但是这个方法很明显的线程安全问题。

那么我们为了解决线程安全问题最简单的方法,就是加上synchronized

public class LazySingleton{
    private static lazySingleton instance = null;
    private lazySingleton(){}
    public synchronized static LazySingleton getInstance(){
        instance = new LazySingleton();
        return instance;
    }
}

但是,这线程安全虽然保证了,却有明显的性能问题,这把同步锁锁在了类上,那么势必会造成使用的时候造成范围较大的挂起,那么我们可以尝试将synchronized放到方法里面,使用块来做。

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

这应该来说,已经是非常好的写法了,因为我们保证了他一定是同一个对象,不过,如果我们通过多线程调试可以发现,如果当两个线程同时准备进入synchronized块的时候,势必只有一个线程能够拿到对象的锁,另外一个线程被阻塞,直到另一个线程释放锁。当持有锁的线程成功new出了LazySingleton对象的时候,释放锁并离开同步块,另外一个在外边等待的线程拿到锁进入同步块的时候,其实又再一次new了这个对象。

虽然看起来是同一个对象但是,其实是已经初始化了两次。所以这里还是存在有瑕疵的地方,那么如何解决呢?

这里有一种叫做双重锁的写法,即在同步块内再判断一次是否为空,这样就一定保证对象只有一次初始化了。

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

希望看到这里你会发现,我们在处理线程安全和资源浪费的问题上,其实已经花了很多的精力和时间,那么这也是我推荐使用静态内部类的原因,让JVM替我们处理这些繁杂的琐事。

反射破坏与序列化破坏

作为单例模式的的设计者,肯定不希望自己定下的getInstance规则被破坏。

但是你不能排除,就是有这种想法的人去按照自己的意愿去使用你设计的类。

在这里,所谓的破坏即是不用你提供的获得单例的方法而得到你的实例。

其实在上面几种设计中,反射和序列化都可以轻易破坏你的设计。

反射破坏
public static void main(String[] arg){
    
    try{
        Class<?> clazz = LazySinglton.class
        Constructor c = clazz.getDeclareConstructor(null);
        c.setAccessible(true);
        //通过反射初始化
        Object obj = c.newInstance();
        
        Object obj1 = LazySinglton.getInstance();
        
        // obj==obj1  false
        
    }catch(Exception e){
        // todo
    }
    
}

这个情况下,显然违背了系统中只有一个实例的定义,所以为了不让使用反射破坏我们的单例,可以在构造方法里写点判断。

public class LazySingleton{
    private static lazySingleton instance = null;
    private lazySingleton(){
        if(instance!=null){
            throw new RunTimeException("can not create more instance!")
        }
    }
    public static LazySingleton getInstance(){
        instance = new LazySingleton();
        return instance;
    }
}
序列化破坏

为什么序列化可以破坏我们的已经单例呢,这是因为,序列化是不走构造方法的,他的本质是对类进行字节码的重组,直接组装一个类的结构,再被直接加载到JVM中,所以本质上,他不会按照一个类的正常生命周期去走构造方法,自然我们的刚刚的反射措施也就没了意义。

那么如何预防呢,首先,在ObjectInputStream中,有一个readObject方法。也就是从序列化出去的文件中去读取被写在文件中的对象,那么在源码中,默认会调用一个readResolve方法,这是序列化在嗅探,如果被序列的类有重写这个方法,那么所返回的Object就拿这个方法的返回值,并且覆盖之前字节码重组后的对象,虽然本质上还是new了两次,但是这是JDK层面的问题,最后会有GC回收,有点无伤大雅的感觉。

public class LazySingleton{
    public static LazySingleton getInstance(){
        return LazySingletonHolder.LazySingleton;
    }
    // 重写方法
    public Object readResolve(){
        return LazySingletonHolder.LazySingleton;
    }
    private LazySingleton(){}
    private static class LazySingletonHolder{
        public static final LazySingleton = new LazySingleton();
    }
}
注册式单例

其实上面说了那么多,无非就是想要介绍这个注册式单例,这种单例的写法,完美的解决了以上我们说的所有问题。没错,他就是这么强大,以至于以后小伙伴可以直接使用这种方法,而无需有后顾之忧。

注册式单例也有两种的写法,一种是容器缓存,另外一种是枚举登记。

枚举登记
public enum EnumSingleton {
	INSTANCE;
    public static EnumSingleton getInstance(){
    	return INSTANCE;
    }
}

这中枚举的单例,当你想要序列化时,在readObject的底层实现中,判断是如果是枚举的时候,将会调用Enum.valueOf方法,获得带有class类型和枚举位置的枚举实例,而这个枚举实例早就在static块中初始化完成了,这里是用了饿汉单例的设计思想,所以序列化后的依旧还是那个static变量。

而当你想要使用反射的时候,即便是填对了参数,也依旧无法正常实例化,这是因为在Constructor有对newInstance的底层方法做了判断,如果是Enum类型,就会在newInstance报错,阻止你反射暴力使用。

这也是在《Effective Java》中最推荐的单例实现方法。

容器缓存

这个大家可以联想一下spring中的ioc容器,就是很典型的容器单例。

ThreadLocal 单例
public class ThreadLocalSingletion{

	private static final ThreadLocal<ThreadLocalSingletion> tl = 
			new ThreadLocal<ThreadLocalSingletion>(){
				@Override
					protected ThreadLocalSingletion initialValue(){
					//保证在此工作线程操作的值都是唯一的、
						return new ThreadLocalSingletion ();
					}
			};
	private ThreadLocalSingletion (){}//单例的常用做法
	public static ThreadLocalSingletion  getInstance(){
		return tl.get();
	}
}

ThreadLocal 之所以可以完成这种在某个线程中是单个线程的情况,来源于ThreadLocal中有一个ThreadMap,这个map以线程作为key,value为对应值,将不同的线程隔离开,保证在线程之间操作不会存在线程安全问题,其实也是容器单例的体现。

一些总结

单例模式的实现,其实很简单,使用也非常简单,我们不必要对这种模式生搬硬套,而是要找到最适合自己的方式。不然就会有点束缚住了自己。

Python调用CANoe 我们可以创建CANoe应用程序对象,打开配置文件,启动模拟,发送和接收消息,并在完成后关闭CANoe应用程序。这使得我们能够使用Python自动化执行测试任务,并集成CANoe的功能到我们的Python应用程序中。CANoe是一款功能强大的汽车网络仿真和测试工具,它能够模拟和分析汽车网络通信,并提供丰富的测试功能。通过Python编程语言,我们可以方便地调用CANoe的API,实现自动化测试和集成CANoe的功能到我们的Python应用程序中。在CANoe中,我们可以为不同的汽车网络创建配置文件。 阅读详情

相关推荐

AXI4接口说明

ACLK:全局AXI接口时钟输入(建议与系统时钟保持一致)ARESET_n:全局AXI接口复位信号,低电平有效。PS:在HBM AXI接口应用中,此复位端口仅应在数据传输开始前使用。若在传输开始后使用,会导致AXI接口与内存控制器失步(out-of-sync)。当开关启用时,IP核会将所有ARESET_n引脚内部连接在一起——因为内存作为一个整体工作。

rhtul的博客 1522

apollo 7.0——单例设计模式解析

三个要点:单例模式有两种实现方法:分别是懒汉和饿汉模式。懒汉模式,即非常懒,不用的时候不去初始化,所以在第一次被使用时才进行初始化;饿汉模式,即迫不及待,在程序运行时立即初始化。例模式(饿汉模式和懒汉模式),线程安全版本实现:私有化构造函数,以防止外界创建单例类的对象;使用类的私有静态指针变量指向类的唯一实例,并用一个公有的静态方法获取该实例。应用场景:资源共享的情况下,避免由于资源操作时导致的性能或损耗等,如日志文件,应用配置。在单例类定义时实例化,不需要用锁,就可以实现线程安全。 优点:这种写法比较简单

xiaohu的博客 1815

ChromeOS安装教程

ChromeOS,是基于Linux内核研发的ChromiumOS开源系统的闭源系统,最大的特色就是内置了Chrome浏览器,早期主要以Web云端应用程序为主,后来逐渐引入了Android子系统和Linux子系统。本文主要简述摸索的家用笔记本安装ChromeOS安装方法,供各位玩机朋友参考。

weixin_45751495的博客 8113

C++中实现singleton(单例模式)的最简单写法

前几天看了 Java中实现singleton的写法,就想在C++中实现一下,找了很多资料,看了各个牛人写的不同版本,但最后在stack overflow上找到了一个最简单的写法,现在贴出来以供参考: class S { public: static S& getInstance() { static S instance;

飞鸟之家 7067

DeepSeek+Coze扣子搭建智能体(保姆级教程)

DeepSeek+Coze扣子搭建智能体(保姆级教程)

yizhen91y的博客 1万+

前端监控性能指标

文章目录前端监控性能指标性能指标阶段性指标关键性能指标小程序W3C Level 1兼容性常规用法注意事项1、计算HTML文档请求使用 Nav Timing2、计算静态资源使用 getEntriesByType('resource') 代替 getEntries()3、secureConnectionStart 问题4、跨域资源设置响应头 Timing-Allow-Origin5、注意属性值为 0 ...

foolbird 1347

【设计模式】单例模式之类模板实现

/* * Copyright (c) 2018,Fzuim * All rights reserved. * * 文件名称:SingletonObj.h * 摘 要: 单例模式,使用类模板 * 用法示例 DECLARE_SINGLETON(CDestObject); * * 当前版本:1.0 * 作 者: Fzuim * 完成日期:2018年3月8日 */ #pragma once te...

Fzuim的博客 2259

手把手教你用gym训练宇树GO2机器人模型(附完整配置文件)

本文提供了一份详细的实战指南,教你如何使用Gym框架为宇树GO2四足机器人搭建强化学习训练环境。文章深入解析了环境配置、参数调试、奖励函数设置等关键步骤,并分享了避开常见陷阱的经验,旨在帮助开发者高效完成模型训练。

oo7890的博客 322

详解Java设计模式之单例模式(Singleton Pattern)

单例模式只包含一个 Singleton(单例角色)类,在单例类的内部实现只生成一个实例,同时它提供一个静态的getInstance()工厂方法,让客户可以使用它的唯一实例。

Elegance never goes out of fashion 2745

大语言模型在金融风险预警中的推理应用探索

金融行业一直面临着各种风险,如信用风险、市场风险、操作风险等。准确及时的风险预警对于金融机构的稳定运营和投资者的利益保护至关重要。传统的金融风险预警方法主要基于结构化数据和统计模型,虽然在一定程度上能够识别风险,但对于非结构化数据的处理能力有限,难以捕捉到复杂的风险因素和潜在的风险趋势。大语言模型作为自然语言处理领域的重要突破,具有强大的语言理解和生成能力,能够处理各种形式的文本数据。

AI天才研究院 1169

Qt 国产嵌入式操作系统实现文字转语音功能(ekho库)

Qt 国产嵌入式操作系统实现文字转语音功能(TTS)_tts驱动 转文字-优快云博客在继上篇文章之后,甲方粑粑不满意使用eSpeak发出的声音太难听了,这就不得不找个替代品,声音稍微好听了一些。使用ekho开源库。Ekho(余音)是一个免费、开源的中文语音合成软件。它目前支持粤语、普通话(国语)、诏安客语、藏语、雅言(中国古代通用语)和韩语(试验中),英文则通过Festival间接实现。Ekho支持Linux、Windows和Android平台。

希望能帮助大家,希望大家多多支持,你们的支持是我前进的动力。 2754

Java设计模式-单例模式详解(Singleton Pattern)

一、单例设计模式概述1、单例模式简介单例模式(Singleton Pattern)是Java中最常见最简单的设计模式之一。这种设计模式属于五个创建型的一种,他提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象方式,可以直接访问,不需要也不能直接从该类外部进行实例化,即在该类的外部不能直接使用new关键字进行...

超级字节码的博客 694

玩转Docker小游戏项目系列:Docker部署吃豆人经典小游戏

玩转Docker小游戏项目系列:Docker部署吃豆人经典小游戏

jks212454的博客 733

设计模式 Day 1:单例模式(Singleton Pattern)详解

设计模式学习不在于一蹴而就,而在于持续积累与反复思考。本篇作为设计模式学习计划的第一天内容,旨在通过结构化讲解帮助读者牢牢掌握单例模式的核心思想与使用方法。后续每日将采用“”的方式推进,这样开篇,一方面帮助读者快速回忆 Day 1,另一方面也自然引出了 Day 2 的学习重点。

Interview_TC的博客 602

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

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

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Java设计模式详解:单例模式(Singleton Pattern)

单例模式(Singleton Pattern)是设计模式中的一种创建型模式,它保证在任何情况下,一个类都只有一个实例,且提供一个全局访问点,例如一个全局配置对象或日志记录对象等。其关键点在于把类的构造器私有化,防止外部类直接创建对象,如果要创建,则通过公有的静态方法获取类的唯一实例。但它也容易引发一些问题,比如隐藏了类之间的依赖关系,增加了系统的耦合性;这种方式具有较好的双检查锁定,可以保证getInstance()返回的实例是单例的,并同时也延迟了实例化Singleton对象的时间。

qq_38235640的博客 313

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

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

设计模式之【单例模式】详解

单例模式(Singleton Pattern)是一种常用的软件设计模式,其核心思想是确保一个类仅有一个实例,并提供一个全局访问点来获取这个实例。单例模式主要用于控制资源的访问,比如配置文件的读取,数据库的连接等,通过确保这类资源全局只有一个实例,既可以避免对资源的多重占用,又可以减少系统的性能开销。*** 饿汉式:预加载* 静态成员变量*///定义静态成员变量获取本类的实例//私有构造方法,避免通过new关键字来实例化对象,保证只存在一个实例//提供一个公共的访问类,让外界获取该对象。

Shine19的博客 992

探索设计模式——单例模式详解

单例模式的简单讲解

strive_mianyang的博客 2093

设计模式——单例模式详解

单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。在程序中多次使用同一个对象且作用相同的时候,为了防止频繁的创建对象,单例模式可以让程序在内存中创建一个对象,让所有的调用者都共享这一单例对象。............

唯有努力,方可自信! 675

《Java设计模式指南》01:单例模式详解

《Java设计模式指南》 内容导航: 1、定义 2、使用场景 3、语法详解 3.1、定义唯一实例 3.2、自己创建唯一实例 3.3、全局访问方式 4、经典案例 4.1、饿汉单例 4.2、懒汉单例 1、定义 单例模式(Singleton Pattern)是最简单的设计模式之一,属于创建型模式,它提供了一种访问某个类“唯一实例(对象)”的绝佳方式,且可以直接访问,不需要再次实...

西安极客联盟 299

详解设计模式之单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。 注意: 1、单例类只能有一个实例。 2、单例类必须自己创建自己的唯一实例。 3、单例类必须给所有其他对象提供这一实例。 介绍 意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。 主要解

swerfse的博客 1207

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

单例模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。在Java中,单例模式通过以下几种方式实现:将构造方法私有化,防止外部直接创建实例。定义一个静态变量来保存唯一的实例。提供一个公共的静态方法来返回这个实例。

楠寻寻的博客 1191

设计模式之单例模式详解

什么是设计模式? 设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的;简单地说,设计模式就是前人总结出来的,一个非常实用的设计套路;单例模式是校招中最常考的设计模式之一, 其存在的意义是防止一个全局使用的类频繁地创建与销毁,当您想控制实例数目,节省系统资源的时候就要使用单例模式,单例模式包括饿汉式和懒汉式......

MacondoMayor的博客 267

java设计模式之单例模式详解

设计模式是一种思想,适合于任何一门面向对象的语言。共有23种设计模式。 单例设计模式所解决的问题就是:保证类的对象在内存中唯一。 举例: A、B类都想要操作配置文件信息Config.java,所以在方法中都使用了Config con=new Config();但是这是两个不同的对象。对两者的操作互不影响,不符合条件。 解决思路: 1.不允许其他程序使用new创建该类对象。(别人new不可控) 2....

weixin_45119658的博客 223

Java 设计模式:单例模式详解

单例模式的核心目标是:限制一个类只能创建唯一实例,并在整个应用程序中共享该实例。

云淡风轻 1077

JAVA设计模式之单例模式详解

JAVA设计模式之单例模式详解

倚栏静望的博客 351
上一篇: 设计原则-里氏替换原则
下一篇: 设计原则-开闭原则与依赖倒置
PopCandier
博客等级 码龄9年 19粉丝 17原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值