Java 实现单例模式的几种方法

单例模式定义与用途

单例模式是一种常见的软件设计模式,其目的是确保一个类在任何情况下只有一个实例,并提供一个全局访问点供外部获取这个唯一的实例。
这种模式特别适用于那些具有全局状态的场合,如配置管理器、线程池、缓存、对话管理等。

Java 实现单例模式的方法

1. 懒汉式

命名来源: 懒汉式的名称来自于他“懒惰”的特性。与饿汉式不同,懒汉式单例类在被加载时并不立即创建实例,而是等到第一次被使用时才创建。这就像一个“懒汉”,总是推迟行动,直到不得不行动的时候。
实现特定: 懒汉式的优点是它支持延迟加载,既仅在需要时才创建实例,这样可以节省资源。然而,这种方式需要处理线程安全问题,因为在多线程环境下,如果多个线程同时访问这个类的实例化方法,就可能创建出多个实例,这违背了单例模式的初衷。

线程不安全

实现代码:

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

上面的写法在多线程环境下不安全。

线程安全

实现代码:

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

2. 饿汉式

命名来源: 这种方式被称为“饿汉式”是因为它非常“饿”,既在类被加载到内存后就立即实例化单例对象,无论你最终是否需要使用这个实例。这种策略类似于一个“饿汉”,他总是急切地想立即 吃东西,不管是否真正饿了。
实现特点: 饿汉实现简单,因为它在类加载时就完成了实例的初始化,因此也避免了线程同步问题。但它的缺点在于,如果该单例类的实例化过程中需要加载大量资源或执行长时间的初始化,而应用程序又可能并不需要这个实例,那么这种预先加载的方式会导致资源的浪费。

实现代码

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

上面的代码线程安全,但是在类加载时就会创建实例,不是懒加载模式。

3. 双重检查锁定(DCL)

“双重检查锁定”(Double-Checked Locking, 简称 DCL)这个名称描述了这种实现模式的关键特性:在创建对象时进行两次条件检查(是否实例已被创建),这两次检查都是为了确保只有一个实例被创建。这个模式的第一次检查发生在同步代码块之外,如果实例已经被创建,就直接返回该实例,避免了进入同步代码块;只有当实力尚未创建时,才进入同步代码块。在同步代码块内部,实现再次检查以确保在等待获取锁的期间实例没有被创建。这种方法确保了单例的线程安全,同时减少了同步带来的性能损耗。

示例代码

public class DCLSingleton {private static volatile DCLSingleton instance;private DCLSingleton() {}public static DCLSingleton getInstance() {if (instance == null) { // First check (no locking)synchronized (DCLSingleton.class) {if (instance == null) { // Second check (with locking)instance = new DCLSingleton();	}	}}return instance;}
}
为何 DCLSingleton 需要被 volatile 修饰

在 Java 中,volatile 关键字的主要作用是确保变量的修改对所有线程立即可见,并防止编译器对带有该关键字的代码进行重排序。在双重检查锁定模式中,volatile 的使用至关重要,原因包括:

  1. 可见性:使用 volatile 修饰 instance 确保当一个线程修改了 instance 的值(既初始化了单例对象)后,这个修改对于访问该变量的其他线程立即可见。这样,一旦 instance 被初始化,所有线程都能看到实际的 instance 值,避免了返回一个尚未完全构造好的对象。
  2. 防止指令重排序:在 Java 的内存模型中,没有 volatile 修饰的变量可能会被编译器或处理器重排序,导致初始化 Singleton 实例的构造函数调用发生在分配内存的操作之后但是赋值之前。这意味着,一个线程可能看到一个非 null 的 instance 引用,但那个对象可能还没有被完全初始化。使用 volatile 可以防止这种情况,它确保在对象的引用被设置之前,对象的构造函数已经被完全执行完毕。

没有使用 volatile 时的问题

  1. 实例化对象的步骤
    步骤1:分配对象内存空间
    步骤2:初始化对象
    步骤3:设置 instance 指向刚分配的内存地址(此时 instance 不再是 null)
  2. 但是,由于Java内存模型的允许重排序,步骤2和步骤3的顺序可能会交换。这样,其他线程可能会看到一个非null的 instance,但它可能是一个未完全构造的对象。

使用 volatile 的效果

  • volatile 关键字防止了指令重排序,确保步骤2(初始化对象)在步骤3(instance 指向内存空间)之前完成。这样,所有看到非null instance 的线程都会看到一个已经初始化的对象。

步骤举例说明

假设有两个线程 A 和 B,它们同时运行 getInstance() 方法:

  • 线程A:进入 getInstance() 方法,发现 instance 是 null,进入同步块,再次检查 instance 仍为 null。它开始执行 new Singleton(),由于没有使用 volatile,指令重排序可能发生,导致 instance 先被设置为非 null。线程 A 被中断,未完成初始化。
  • 线程B:此时进入 getInstance() 方法,看到 instance 已经不是 null(尽管实例尚未完全初始化),返回这个未完全初始化的对象

这种情况可能导致程序行为出现异常,因此,使用 volatile 是防止此类问题发生的重要机制。

4. 枚举单例模式

枚举单例模式是使用 Java 的枚举类型来实现单例模式的方法。这种方式不仅能自动支持序列化机制而且通过 JVM 从根本上保证反射攻击的安全问题。

实现细节

  • 枚举类的加载:Java 的枚举类的加载和初始化由 JVM 控制。当枚举类被加载时,其定义的枚举值(实例)在类加载时一次性实例化。
  • 线程安全性:由于枚举实例创建是在类加载时完成的,由 JVM 保证其线程安全性。因此,使用枚举方式实现的单例模式无需担心多线程并发和同步问题。
  • *防止通过反射或序列化破坏单例:枚举类默认禁止通过反射创建对象,同时每个枚举实例都是通过枚举类自动维护的静态实例来管理,因此反序列化时不会重新创建实例。

示例代码

public enum Singleton {INSTANCE;public void someServiceMethod() {System.out.println("Performing a service");}
}

5. 静态内部类单例模式

静态内部类单例模式是通过内部类的特性和类的加载机制来确保单例只被创建一次,同时实现延迟加载和高效性。

实现细节

  • 静态内部类:在外部类加载的时候,并不会立即加载内部类,内部类不会在外部类加载时立即加载,而是在需要实例化时,调用 getInstance() 方法,从而导致 SingletonHolder.INSTANCE 被引用,这才会导致内部类加载和初始化其静态成员。
  • 类初始化的线程安全性:类的初始化是由 JVM 来控制的,确保一个类的静态初始化器在多线程环境中被正确地加载、链接和初始化。这就像是由 JVM 提供的一个同步锁。
  • 延迟加载和性能:这种方式实现了延迟加载和高效性。单例实例仅在第一次被使用时创建,并且创建过程由 JVM 的类初始化机制保证其线程安全性。

代码示例

public class Singleton {private Singleton() {}private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}

总结:

Java实现单例模式主要有以下几种方式:

  1. 饿汉式(静态常量):在类加载时就完成了实例化,避免了线程同步问题。
  2. 懒汉式(线程安全,同步方法):在第一次调用时实例化对象,实现了懒加载。
  3. 双重检查锁定(DCL,double-checked locking):在懒汉式的基础上,通过双重检查锁定机制保证了线程安全和懒加载。
  4. 静态内部类(推荐):利用静态内部类的特性,实现了懒加载且线程安全。
  5. 枚举(推荐):通过枚举类型实现单例,简洁且线程安全。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/46490.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

负载均衡 lvs

1. 4层转发(L4) 与 7层转发(L7) 区别 4层转发(L4) 与 7层转发(L7) 区别 转发基于的信息 状态 常用的服务 L4 基于网络层和传输层信息: L4转发主要依赖于网络层IP头部(源地址,目标地址,源端口,目标端口)和传输层头部&#xff…

珈和科技完成全国首个农险服务类数据产品入表,实现数据资产化

近日,珈和科技与东湖大数据合作,完成全国首个保险服务类数据产品入表,标志着我国商业卫星遥感应用领域迈出了数据资产化的关键一步。 此次入表的数据产品为“华北农业保险服务数据集数据产品”,是珈和科技融合卫星遥感与无人机等…

新华三H3CNE网络工程师认证—VLAN使用场景与原理

通过华三的技术原理与VLAN配置来学习,首先介绍VLAN,然后介绍VLAN的基本原理,最后介绍VLAN的基本配置。 一、传统以太网问题 在传统网络中,交换机的数量足够多就会出现问题,广播域变得很大,分割广播域需要…

前端学习(二)之HTML

一、HTML文件结构 <!DOCTYPE html> <!-- 告诉浏览器&#xff0c;这是一个HTML文件 --><html lang"en"> <!-- 根元素&#xff08;起始点&#xff0c;最外层容器&#xff09; --><head> <!-- 文档的头部&#xff08;元信息&#xff…

Typora 1.5.8 版本安装下载教程 (轻量级 Markdown 编辑器),图文步骤详解,免费领取

文章目录 软件介绍软件下载安装步骤激活步骤 软件介绍 Typora是一款基于Markdown语法的轻量级文本编辑器&#xff0c;它的主要目标是为用户提供一个简洁、高效的写作环境。以下是Typora的一些主要特点和功能&#xff1a; 实时预览&#xff1a;Typora支持实时预览功能&#xff0…

实战篇(十一) : 拥抱交互的三维世界:利用 Processing 和 OpenGL 实现炫彩粒子系统

🌌 拥抱交互的三维世界:利用 Processing 和 OpenGL 实现炫彩粒子系统 在现代计算机图形学中,三维粒子系统是一个激动人心的领域。它不仅可以用来模拟自然现象,如烟雾、火焰和水流,还可以用来创造出令人叹为观止的视觉效果。在这篇文章中,我们将深入探讨如何使用 Proces…

【linux】服务器安装NVIDIA驱动

【linux】服务器安装NVIDIA驱动 【创作不易&#xff0c;求点赞关注收藏】&#x1f600; 文章目录 【linux】服务器安装NVIDIA驱动一、关闭系统自带驱动nouveau二、下载英伟达驱动三、安装英伟达驱动1、禁用X服务器和相关进程2、在TTY终端安装驱动3、验证是否安装成功4、重新启…

最新开源的解析效果非常好的PDF解析工具MinerU (pdf2md pdf2json)

毫不夸张的说 PDF解析工具MinerU是照进RAG黑暗中的一道光——这是我对它的评价。我测过太多了文档解析工具&#xff01; 最近在做文档解析的工作。看了很多的开源的文档解析的工具&#xff0c;版面分析的工具&#xff0c;其中包括paddelpaddel这样30kstar的明星工具。但是效果都…

01 安装

安装和卸载中&#xff0c;用户全部切换为root&#xff0c;一旦安装&#xff0c;普通用户也能使用 初期不进行用户管理&#xff0c;全部用root进行&#xff0c;使用mysql语句 1. 卸载内置环境 检查是否有mariadb存在&#xff0c;存在走a部分卸载 ps axj | grep mysql ps ajx |…

逻辑门的题目怎么做?

FPGA语法练习——二输入逻辑门&#xff0c;一起来听~~ FPGA语法练习——二输入逻辑门 题目介绍&#xff1a;F学社-全球FPGA技术提升平台 (zzfpga.com)

低代码中间件学习体验分享:业务系统的创新引擎

前言 星云低代码平台介绍 星云低代码中间件主要面向企业IT部门、软件实施部门的低代码开发平台&#xff0c;无需学习开发语言/技术框架&#xff0c;可视化开发PC网页/PC项目/小程序/安卓/IOS原生移动应用&#xff0c;低门槛&#xff0c;高效率。针对企业研发部门人员少&#…

什么是正则表达式,如何在 Python 中使用?

什么是正则表达式 正则表达式&#xff08;Regular Expression&#xff0c;简称Regex&#xff09;是一种用于匹配字符串中字符模式的工具。它是由普通字符&#xff08;例如字母、数字&#xff09;以及一些特殊字符&#xff08;称为元字符&#xff09;组成的字符序列。这种模式用…

Spring MVC-什么是Spring MVC?

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 今天你敲代码了吗 文章目录 1.MVC定义2. Spring MVC 官方对于Spring Web MVC的描述这样的: Spring Web MVC is the original web framework built on the Servlet APl and has been includedin the Spring Frame…

node解析Excel中的考试题并实现在线做题功能

1、背景 最近公司安排业务技能考试&#xff0c;下发excel文件的题库&#xff0c;在excel里查看并不是很方便&#xff0c;就想着像学习驾考题目一样&#xff0c;一边看一边做&#xff0c;做完之后可以查看正确答案。 2、开始分析需求 题目格式如下图 需求比较简单&#xff0c;…

【二叉树】【动态规划】1、斐波那契数+2、零钱兑换

1、遍历&#xff1a;在遍历的过程中就能够解决问题&#xff0c;只需要递归函数的参数即可。 2、子树&#xff1a;只有在遍历完成之后才能解决问题&#xff0c;还需要递归函数的返回值。&#xff08;需要在后序位置写代码&#xff09; 动态规划&#xff1a;子树 核心思想是穷举…

Leetcode 3217. Delete Nodes From Linked List Present in Array

Leetcode 3217. Delete Nodes From Linked List Present in Array 1. 解题思路2. 代码实现 题目链接&#xff1a;3217. Delete Nodes From Linked List Present in Array 1. 解题思路 这一题是一个十分典型的链表的问题&#xff0c;我们注意区分一下头节点和中间节点即可。 …

【electron】 快速启动electron 应用

学无止境&#xff1a; 最近在搞electron项目&#xff0c;最重要的是总结 &#xff0c;写下来总不会忘记&#xff0c;也希望给大家参考一下&#xff0c;有不对的地方希望大家多指点。 快速启动electron 应用 1 克隆示例项目的仓库 git clone https://github.com/electron/ele…

【Oracle】Oracle中的LISTAGG函数

目录 解释与其他聚合函数的区别 使用场景语法分析函数用法聚合函数用法对比 示例分析函数使用案例聚合函数使用案例 优缺点LISTAGG函数的优点&#xff1a;LISTAGG函数的缺点&#xff1a; 注意事项 解释 LISTAGG函数是一种用于字符串连接的聚合函数&#xff0c;可以将多行的值进…

基于最新版的flutter pointycastle: ^3.9.1的AES加密

基于最新版的flutter pointycastle: ^3.9.1的AES加密 自己添加pointycastle: ^3.9.1库config.dartaes_encrypt.dart 自己添加pointycastle: ^3.9.1库 config.dart import dart:convert; import dart:typed_data;class Config {static String password 成都推理计算科技; // …

Hadoop-36 HBase 3节点云服务器集群 HBase Shell 增删改查 全程多图详细 列族 row key value filter

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; HadoopHDFSMapReduceHiveFlumeSqoopZookeeperHBase 正在 章节内容 上一节我们完成了&#xff1a; 集群的…