单例模式

简介: 本文主要介绍内容:1. 单例及单例模式概念2. 实现单例模式注意点3. 单例模式的实现方式

单例及单例模式概念

一个类只允许创建一个实例。这种设计模式就是单例模式

         1617762041548.png


实现单例模式需要考虑什么?

  • 构造函数私有,避免外部通过new来创建实例
  • 考虑是否需要延时加载
  • 考虑线程安全


单例模式的几种实现方式

1. 饿汉式:在类加载的时候,就已经初始化实例了。

publicclassHungry {
// 一启动就被加载,还未使用就占用内存,造成资源浪费privatestaticfinalHungryINSTANCE=newHungry();
// 构造方法私有,外部无法进行实例化privateHungry() {}
// 获取实例对象publicstaticHungrygetInstance() {
returnINSTANCE;
  }
}


2. 懒汉式:第一次使用时才进行实例化

publicclassLasy {
// 启动时不加载,需要时再进行实例化privatestaticLasyINSTANCE;
// 构造方法私有,外部无法进行实例化privateLasy() {}
publicstaticLasygetInstance() {
if (null==INSTANCE) {
INSTANCE=newLasy();
        }
returnINSTANCE;
    }
}

但是懒汉式并不能保证单例,在多线程并发情况下,会多次调用构造方法进行实例化,示例如下:

publicclassLasy {
// 启动时不加载,需要时再进行实例化privatestaticLasyINSTANCE;
// 构造方法私有,外部无法进行实例化privateLasy() {
System.out.println(Thread.currentThread().getName());
    }
publicstaticLasygetInstance() {
if (null==INSTANCE) {
INSTANCE=newLasy();
        }
returnINSTANCE;
    }
publicstaticvoidmain(String[] args) {
for (inti=0; i<10; i++) {
newThread(Lasy::getInstance).start();
        }
    }
}

输出结果:

2.png


2.1 使用同步锁synchronized

//如果使用synchronized关键字,这种方式在并发下并发量等同于1,如果频繁使用该对象,会大大降低性能publicstaticsynchronizedLasygetInstance() {
if (null==INSTANCE) {
INSTANCE=newLasy();
    }
returnINSTANCE;
}


2.2 双重检测(DCL懒汉式单例)

publicstaticLasygetInstance() {
if (null==INSTANCE) {
synchronized (Lasy.class) {
if (null==INSTANCE) {
INSTANCE=newLasy();
            }
        }
    }
returnINSTANCE;
}
// INSTANCE = new Lasy();/** 以上操作并不是原子性操作,包括以下三个步骤:* 1. 分配内存空间* 2. 执行构造方法进行初始化* 3. 将对象指向这个空间*//** 在多线程情况下可能出现指令重排,每个线程执行INSTANCE = new Lasy()这个操作的顺序可能不一致,那么就有可能出现线程A先执行了第一步和第三步,此时还未进行初始化,线程B执行判断语句null == INSTANCE时为false,将直接返回线程A未进行初始化的INSTANCE对象,所以要避免指令重排,使用volatile关键字。*/privatestaticvolatileLasyINSTANCE;
publicstaticLasygetInstance() {
if (null==INSTANCE) {
// 对象未被实例化,等待类对象锁synchronized (Lasy.class) {
// 进入对象锁,其他线程等待,再次判断是否被实例化if (null==INSTANCE) {
INSTANCE=newLasy();
            }
        }
    }
returnINSTANCE;
}

饿汉式和懒汉式比较:
①由于饿汉式是在类加载的时候进行实例化,线程安全;懒汉式是在第一次使用的时候进行实例化,此时可能多线程会同时进行实例化,所以线程不安全;
②饿汉式不支持延时加载,如果占用资源较多或者耗时较长,可能会影响启动时间;懒汉式支持延时加载,但是需要使用同步锁,如果这个类被频繁使用,可能导致性能问题。(相比较更加倾向于饿汉式单例,在启动时进行加载,如果遇到性能或者资源问题会提前暴露,相较于正在运行中出现问题更加可控)


3. 静态内部类

publicclassTest {
privateTest() {}
privatestaticclassInnerTest{
// final修饰变量是不可变的,初始化对象之后便不会再指向另一个对象privatestaticfinalTestINSTANCE=newTest();
    }
publicstaticTestgetInstance() {
returnInnerTest.INSTANCE;
    }
}

但是在反射机制下,所有的私有方法都能被破解,如下实例表示通过静态内部类获得的实例对象和通过反射获得的holder实例对象并不是同一个,仍然破坏了单例

publicstaticvoidmain(String[] args) throwsException {
// 获取private的构造方法Constructor<Test>constructor=Test.class.getDeclaredConstructor(null);
// 无视私有构造器constructor.setAccessible(true);
Testtest=constructor.newInstance();
System.out.println(Test.getInstance().equals(test));
}
输出结果:false

通过反射的源码可以看到,无法反射式创建枚举对象,所以我们可以考虑使用枚举来实现单例

1.png

4. 枚举

publicenumSingleEnum {
INSTANCE;
}


以下示例可见确实无法反射式创建枚举对象,当使用反射创建枚举对象时,会抛出异常信息Cannot reflectively create enum objects

publicstaticvoidmain(String[] args) throwsException {
// 获取private的构造方法(实际上枚举类的构造方法带有两个参数,并不是我们从源码看到的无参构造,需要使用源码工具来查看)Constructor<SingleEnum>constructor=SingleEnum.class.getDeclaredConstructor(String.class, int.class);
// 无视私有构造器constructor.setAccessible(true);
SingleEnumsingle=constructor.newInstance();
System.out.println(SingleEnum.INSTANCE.equals(single));
}

输出:

3.png

相关文章
|
Web App开发 关系型数据库 RDS
电源缓启动(软起动)原理
该文讨论了电源的缓启动(软起动)技术,主要是为了解决热插拔过程中可能产生的电源振荡和大电流冲击问题。缓启动通过防抖动延时和控制电流上升斜率来避免系统受影响或设备受损。文章提到了两种类型的缓启动电路:电压斜率型和电流斜率型,并详细解释了电压型缓启动电路的工作原理,包括各个元件的作用和电路的缓启动阶段。
466 12
|
人工智能 搜索推荐 算法
玩转通义星尘:体验定制化多样角色能力
在杭州云栖大会上,阿里云对外展示了一款个性化角色创作平台——**通义星尘**,其基于大规模高质量个性化对话数据,采用分阶段的个性化训练策略,使得模型在保持通用能力的基础上,延伸出拟人、具有情感、鲜明语言风格的能力,在角色的个性、风格遵循上具有更强的指令遵循能力。那么其能力展现到底如何?我们又能玩出哪些花样呢?今天开始测试通义星尘,争取年前把8个垂直模型都测试一遍,,加油!本文为原创,未经许可请勿搬运。
玩转通义星尘:体验定制化多样角色能力
|
运维 Kubernetes 安全
云原生安全 — seccomp应用最佳实践
近期针对Linux内核的CVE漏洞频出,CVE-2022-0185、CVE-2022-0185、CVE-2022-0847是威胁评分较高且热度较高的几个典型漏洞,相关的POC/EXP利用代码也已经在互联网上公开披露。对于容器场景来说,攻击者的攻击路径也比较相似,都是利用unshare等高危系统调用在新的usernamespace拿到CAP_SYS_ADMIN等高权限capabilities后利用漏
2967 0
云原生安全 — seccomp应用最佳实践
|
12月前
|
SQL 关系型数据库 MySQL
案例剖析,MySQL共享锁引发的死锁问题!
案例剖析,MySQL共享锁引发的死锁问题!
148 0
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp小程序的私房菜定制上门服务系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp小程序的私房菜定制上门服务系统附带文章源码部署视频讲解等
198 0
|
前端开发
css动画效果(边框流光闪烁阴影效果)
css动画效果(边框流光闪烁阴影效果)
|
移动开发 数据可视化 前端开发
低代码引擎核心技术,可视化动作——OneCode技术实践
低代码平台最大的一个技术特点便是开发图形化、可视化,通过拖拉拽方式快速实现企业数字化转型中的创新应用。在实践中通过图形化技术确实在一些特定领域大幅降低了应用开发的准入门槛,使得非专业人员也可以快速的参与到企业的数字化转型中。但随着业务的深入个性化需求也进一步增多,多数的低代码平台都无法满足相关的逻辑,这时仍然需要专业的程序员通过代码的方式来扩展。 但这些业务逻辑的代码繁琐且无用,只能让程序员在做低水平的重复工作。有痛点就会有需求,一些低代码平台推出了可视化逻辑编排能力,能够很好地解决这个问题。本文将结合OneCode平台的可视化逻辑编排设计来进行分析,希望对你有帮助。
|
网络协议 物联网 网络架构
数据通信网络之 IPv6 基础
数据通信网络之 IPv6 基础
427 0
|
机器学习/深度学习 传感器 算法
pytorch实现循环神经网络实验
pytorch实现循环神经网络实验
488 0
|
缓存 网络协议 Oracle
Spring集成H2内存数据库
H2内存数据库使用,满足缓存关系型数据库的使用,快速上手,无缝衔接oracle、mysql
Spring集成H2内存数据库