Java设计模式-创建者模式-单例模式

news/2024/10/25 7:27:57/

单例模式

  • 单例模式
    • 饿汉式
    • 懒汉式

单例模式

解释:一个类只能有一个实例
单例模式可以分为两种 饿汉式懒汉式

饿汉式

也被称为预加载,即 在加载类的时候,就将实例创建出来,加载到内存,不管之后会不会使用这个实例
主打一个饥不择食,体现了贪心的思想。

public class HungrySingleton {//方式1:静态变量private static HungrySingleton instance = new HungrySingleton();//方式2:静态代码块/*static {instance = new HungrySingleton();}*///方式3:枚举,可以看 EnumSingleton.classpublic static HungrySingleton getInstance(){return instance;}
}

很明显,我们还没有使用该对象,就已经加载到了内存,浪费内存
但是,同时,因为只有一此创建对象,所以饿汉式是线程安全的

懒汉式

也成为懒加载,即:只有在使用该类时才创建需要的对象
我很懒,你不用我,我就不创建对象

一般有四种实现方式

  1. 简单懒汉式,不能用,线程不安全
  2. Synchronized 同步方法,一般不用,线程安全,但锁粒度太大,效率较低
  3. 双重检查锁,可以用,注意使用volatile 关键字保证单例对象的原子性
  4. 静态内部类,推荐使用
public class LazySingleton {private volatile static LazySingleton instance;/*** 简单懒汉式* 问题:线程不安全,一般不用* @return*/public static LazySingleton getInstance01() {if(null == instance){instance = new LazySingleton();}return instance;}/*** synchronized* 解决了线程安全问题,但效率低,一般不用* @return*/public synchronized static LazySingleton getInstance02() {if(null == instance){instance = new LazySingleton();}return instance;}/*** 双重检查锁模式* 降低锁的粒度,只锁创建对象的代码块* 需要增加 volatile 来保证原子性,防止jvm指令重排,但同时屏蔽了JVM的一些代码优化* @return*/public static LazySingleton getInstance03() {if(null == instance){synchronized (LazySingleton.class){if(null == instance){//防止重复创建对象instance = new LazySingleton();}}}return instance;}/*** 静态内部类方式* 只有在使用时才会创建静态内部类,推荐使用* @return*/public static LazySingleton getInstance04() {return SingletonHolder.INSTANCE;}private static class SingletonHolder{private static final LazySingleton INSTANCE = new LazySingleton();}
}

这里解释下 为什么 双重检查锁要 使用 volatile 关键字 修饰单例对象
if判断以及其内存执行代码是非原子性的。其次,new LazySingleton()无法保证执行的顺序性。
显然,不满足原子性或者顺序性,线程肯定是不安全的。
下面主要讲一下 new LazySingleton() 为什么不能保证顺序性。
设想一下,创建一个对象,应该分为几步?
答案是三步,如下:

memory=allocate();//1:初始化内存空间
ctorInstance(memory);//2:初始化对象
instance=memory();//3:设置instance指向刚分配的内存地址

jvm为了提高程序执行性能,会对没有依赖关系的代码进行重排序,也就是说上面2和3行代码可能被重新排序。
我用两个线程举例:

时间片线程A线程B
t1初始化内存空间
t2设置instance指向刚分配的内存地址
t3判断instance 是否为空
t4由于instanc不为空,获取到一个空的实例化对象(线程不安全
t5初始化对象

volatile 关键字就是强制要求jvm 不进行指令重排,按顺序执行,当然也有一定的性能损失
volatile 其他相关知识 可以参考以下文章:
https://blog.csdn.net/m0_50370837/article/details/124380385


http://www.ppmy.cn/news/1207823.html

相关文章

共话医疗数据安全,美创科技@2023南湖HIT论坛,11月11日见

11月11日浙江嘉兴 2023南湖HIT论坛 如约而来 深入数据驱动运营管理、运营数据中心建设、数据治理和数据安全、数据资产“入表”等热点、前沿话题 医疗数据安全、数字化转型深耕者—— 美创科技再次深入参与 全新发布:医疗数据安全白皮书 深度探讨:数字…

【Git】推送Github失败:remote: Permission to xxx/*.git denied to xxx

在github上&#xff0c;创建了token&#xff0c;推送代码报没权限 #设置token git remote set-url origin <your.token>github.com/<your.name>/hello-git.git#推送代码 #git push -u origin main remote: Permission to xxx/hello-git.git denied to xxx. fatal:…

变电站自动化系统中的安全措施分析及应用-安科瑞

安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a;阐述变电运行中的问题&#xff0c;电气自动化系统与安全运行措施&#xff0c;包括自动控制设备的投入&#xff0c;电气自动 化与计算机技术相、设备数据的采集与处理、自动化系统的升级、人工智能技术的应用。 关键…

毅速丨为什么不锈钢材料在金属3D打印中应用广泛

不锈钢材料作为一种常见材料&#xff0c;在金属3D打印中应用广泛&#xff0c;可以说是目前使用率最高的材料&#xff0c;为什么不锈钢大受欢迎&#xff0c;主要由几点原因。 第一、工艺适合性 金属3D打印的工艺&#xff0c;如直接金属激光烧结&#xff08;DMLS&#xff09;或选…

前后端交互常见的几种数据传输格式 form表单+get请求 form表单+post请求 json键值对格式

目录 1. get请求 query string 2.form表单get请求 3..form表单post请求 4..json格式 5.总结 1. get请求 query string 前端通过get请求携带 query string&#xff08;键值对&#xff09; ,后端通过req.getParameter(key)方法获取数据。如果key不存在&#xff0c;获取到的就…

C++学习笔记---命名空间namespace

文章目录 前言简介定义命名空间using指令不连续&嵌套的命名空间 前言 wu 简介 C中的命名空间&#xff08;Namespace&#xff09;是一种用于组织和封装代码的机制&#xff0c;它允许你将一组相关的函数、类、变量和其他标识符放置在一个逻辑分组内&#xff0c;以便在不同的…

Centos 64位环境下编译32位C程序

在64位平台下&#xff0c;默认编译的是64位程序。如果需要编译32位程序&#xff0c;需要指定gcc 编译选项“-m32”。 但是&#xff0c;在编译过程中&#xff0c;往往会碰到这样的错误&#xff1a;atal error: gnu/stubs-32.h: No such file or directory。 在链接过程中&#x…

java正则表达式(Pattern类和Matcher类)

文章目录 前言一、Java中使用正则表达式①捕获组②Pattern类与Matcher类Pattern类&#xff1a;Matcher类&#xff1a;Pattern类&Matcher类方法:Pattern类调用方法&#xff08;静态方法&#xff09;&#xff1a;1、public static Pattern compile(String regex)2、public st…