当前位置: 首页 > news >正文

设计模式之单例模式大全---java实现

饿汉式:

概述:

        1.饿汉式是单例模式中最简单、最直接的一种实现方式。

        2.它的核心思想在于:“我饿了,我现在就要吃!” —— 也就是说,不管程序需不需要,我都

           先在类加载的时候就把这个单例实例创建好,等着你来用。

        3.这种“急切”的初始化方式,因此得名“饿汉式”。

代码及注释解释:

就是一上来先创建实例对象,构造器私有,然后通过方法return这个实例对象

public class EagerSingleton {// 1. 静态常量:在类加载阶段,就由JVM初始化这个唯一实例。//    关键字 `final` 可选,但可以明确表示此实例不可改变,增强可读性。private static final EagerSingleton INSTANCE = new EagerSingleton();// 2. 私有构造函数:防止外部通过 `new` 关键字创建实例。private EagerSingleton() {// 防止在反射攻击下被多次实例化(一种简单的防护,但并非绝对安全)if (INSTANCE != null) {throw new RuntimeException("单例对象不能重复创建");}System.out.println("EagerSingleton is initialized!");}// 3. 全局静态访问点:提供给外部获取这个唯一实例的方法。public static EagerSingleton getInstance() {return INSTANCE;}
}

工作原理 

饿汉式的线程安全性并不是通过同步代码(synchronized) 来实现的,而是利用了 JVM 的类加载机制

  • JVM 在加载一个类时,会初始化其静态变量(static)。

  • 这个过程是线程安全的,因为 JVM 保证了每个类只会被加载一次,其静态变量自然也只会被初始化一次。

  • 因此,INSTANCE 这个单例对象在类加载的初始化阶段就被创建出来了,后续所有调用 getInstance() 的方法都只是返回这个早已创建好的对象的引用。

优缺点:

优点
  • 实现简单:代码非常简洁明了,易于理解。

  • 线程安全:无需任何同步措施,由 JVM 底层机制保证线程安全,性能无忧。

 缺点
  • 可能造成资源浪费:这是饿汉式最大的问题。如果这个单例实例的构造过程很耗时(比如加载大量数据、连接资源),或者这个实例在程序运行过程中根本就没被用到,那么它的提前初始化就是一种不必要的内存和计算资源的浪费。

  • 非懒加载 (Lazy Loading):无法做到“用时再创建”,不符合一些特定场景的需求

适用场景

  1. 内存占用小:实例本身占用的内存资源不大,即使不用,也能接受其常驻内存。

  2. 几乎每次运行都会用到:你几乎可以肯定这个实例在程序运行中一定会被使用到。

  3. 追求极致的性能:在超高并发环境下,虽然双重检查锁(DCL)性能也很好,但饿汉式的 getInstance() 方法没有任何同步开销,理论上是最快的。


懒汉式:

.核心概念

        1.懒汉式的核心思想是:“我很懒,只有到你真正需要我的时候,我才会动手创建自己。”

        2.这与饿汉式的“迫不及待”形成鲜明对比。它避免了提前初始化可能造成的资源浪费,只有在

           第一次调用 getInstance() 方法时才会创建实例。

        3.这种“用时再创建”的方式也称为,懒加载,延迟加载 (Lazy Loading)

版本推演以及代码实现

版本1(线程安全问题):

这是最原始的版本,但是面临线程不安全的问题,

假设线程1进入if语句后,还没有执行完创建对象实例的操作

此时线程2也进入了判断就出现了线程冲突

public class UnsafeLazySingleton {private static UnsafeLazySingleton instance;private UnsafeLazySingleton() {}public static UnsafeLazySingleton getInstance() {// 线程不安全的关键点!if (instance == null) { // 【步骤1】:线程A和线程B同时检查,都发现instance为nullinstance = new UnsafeLazySingleton(); // 【步骤2】:它们都会执行这行代码,导致创建多个实例}return instance;}
}
版本二(解决线程安全 但存在效率问题):

加上关键字后 表明在线程1没执行完时线程2不会进入,会等待

但是这表明,在同一时间内只能有一个线程执行,效率大打折扣,所以也不推荐

public class SynchronizedLazySingleton {private static SynchronizedLazySingleton instance;private SynchronizedLazySingleton() {}// 使用synchronized关键字修饰整个方法public static synchronized SynchronizedLazySingleton getInstance() {if (instance == null) {instance = new SynchronizedLazySingleton();}return instance;}
}
版本三(双重检查锁)

此时将关键字放在了最外层判断里面,就算有线程同时进入判断也会先等待,

等前一个创建完后在进行判断,不会重复创建实例,

而其他线程直接在最外层判断被拦截,相比于版本二极大的提高了效率

public class DCLSingleton {// 关键:使用volatile关键字禁止指令重排序private static volatile DCLSingleton instance;private DCLSingleton() {}public static DCLSingleton getInstance() {// 第一次检查:避免不必要的同步if (instance == null) {// 同步代码块synchronized (DCLSingleton.class) {// 第二次检查:确保在同步块内再次判断,防止多次创建if (instance == null) {instance = new DCLSingleton(); // volatile 关键字在此处至关重要!}}}return instance;}
}

注:在声明instance时为什么需要volatile??

  • 语句 instance = new DCLSingleton(); 并不是一个原子操作,它大致分为三步:

    1. 为对象分配内存空间

    2. 初始化对象(调用构造函数等)

    3. 将 instance 引用指向分配的内存地址

  • JVM 可能会进行指令重排序,将步骤2和步骤3调换顺序。如果线程A执行了1和3,但尚未执行2,此时 instance 已不为 null(但对象未初始化)。

  • 此时如果线程B执行第一重检查 if (instance == null),会发现 instance 不为 null,于是直接返回这个半成品对象,从而导致程序出错。

  • volatile 关键字的作用就是禁止JVM对此语句进行指令重排,从而保证线程安全。

版本四(静态内部类):

这被称为最优雅的懒加载

利用了jvm的机制,在加载外部类时,内部类不会被加载,只有在调用方法时,

才开始加载静态内部类,这个过程是线程安全的,同时效率也提升了。

public class InnerClassSingleton {private InnerClassSingleton() {}// 静态内部类private static class SingletonHolder {private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();}public static InnerClassSingleton getInstance() {return SingletonHolder.INSTANCE;}
}

枚举的方式:

核心概念

        1.用枚举实现单例模式,可以说是 《Effective Java》 这本书作者 Joshua Bloch 提倡的、

        2.目前实现单例的最佳实践。它的核心思想是:利用Java枚举类型本身的特性,让JVM来为

           我们保证单例的绝对唯一性,并防御各种攻击。

代码实现及使用(及其简单)

public class SingletonTest3 {public static void main(String[] args) {Singleton1 instance1 = Singleton1.INSTANCE;instance1.say();}
}
enum Singleton1 {INSTANCE;public void say() {System.out.println("ok");}
}

java中使用单例模式的源码例子:

在Runtime类中可以看出,它使用了饿汉式:


文章转载自:

http://SdpAFyBT.xfLzm.cn
http://ZFAIO6bj.xfLzm.cn
http://UZiDWOtA.xfLzm.cn
http://yatD3qGW.xfLzm.cn
http://DCKeLjSL.xfLzm.cn
http://HQM6AR8o.xfLzm.cn
http://e5Cm3C4P.xfLzm.cn
http://KoTLyA9v.xfLzm.cn
http://b9SFUStF.xfLzm.cn
http://1WHR9Wvd.xfLzm.cn
http://TmcIAPV1.xfLzm.cn
http://ALIK37EE.xfLzm.cn
http://PqJ8lX9m.xfLzm.cn
http://GiXpwhNV.xfLzm.cn
http://9YlfXLYm.xfLzm.cn
http://hv7M8jps.xfLzm.cn
http://IxwJSvB4.xfLzm.cn
http://0knTU4ka.xfLzm.cn
http://8YeyfYBw.xfLzm.cn
http://EKX2KNZO.xfLzm.cn
http://mDR9E5tb.xfLzm.cn
http://iHfkwQOv.xfLzm.cn
http://2BlCIV0I.xfLzm.cn
http://CG0m7jOW.xfLzm.cn
http://yTZ0vQLN.xfLzm.cn
http://4vH1uZST.xfLzm.cn
http://bVdTtfFv.xfLzm.cn
http://XJ3icmMZ.xfLzm.cn
http://iTYkczHY.xfLzm.cn
http://zVOLJPXz.xfLzm.cn
http://www.dtcms.com/a/372920.html

相关文章:

  • Separated collaborative learning
  • Microsoft Visual C++ 运行库安装教程(最新版完整指南 | DLL修复方案)
  • 【Linux】vim工具篇
  • 迁移学习:人工智能的智慧传承与革新之道
  • SQL面试题及详细答案150道(116-135) --- 高级查询与函数篇
  • LRU 缓存
  • Redis的Feed流实现方案
  • 5G专网、物联网专业技术知识
  • LaTeX TeX Live 安装与 CTAN 国内镜像配置(Windows / macOS / Linux 全流程)
  • 脑电数据预处理十六:自动化阈值检测原理与实践
  • 9月8日
  • Java全栈开发工程师的实战面试经历:从基础到微服务
  • 冰火岛 Tech 传:Apple Foundation Models 心法解密(上集)
  • 腾讯云 CLB (Cloud Load Balancer) 为例,详细讲解如何配置 Nginx 集群
  • 25.线程概念和控制(二)
  • VMware-三种网络模式原理
  • 【Java实战㉝】Spring Boot实战:从入门到自动配置的进阶之路
  • 【WRF-VPRM 预处理器第一期】完整安装(服务器)
  • 【Unity笔记】Unity 编辑器扩展:打造一个可切换 Config.assets 的顶部菜单插件
  • Visual Studio Code设置个性化背景教程
  • AI内容标识新规实施后,大厂AI用户协议有何变化?(二)百度系
  • 【大模型应用开发 5.LlamaIndex知识管理与信息检索】
  • 打开SOLIDWORKS非常缓慢的测试排查方法
  • 《Redis Cluster 去中心化实战指南:从集群搭建、故障转移到扩缩容全流程》
  • 大厂的服务器自动扩缩容
  • 02OpenCV基本操作
  • 在Word和WPS文字中将手机中间4位替换为星号****
  • Chrome的“无处不在”与推动Web平台演进的使命
  • 开源PSS解析器1
  • 软件OS研发行业人机料法环应用总结:基于鱼骨图的分析框架