如何在 Java 中实现线程安全的单例模式?

ops/2024/9/18 6:33:08/ 标签: 单例模式, java, 安全

单例模式是一种常用的软件设计模式,它确保一个类只有一个实例,并提供一个全局访问点。

在多线程环境下,确保单例模式的线程安全性是非常重要的,因为多个线程可能会同时尝试创建实例,导致实例不唯一的问题。

单例模式的实现方法

在 Java 中实现线程安全单例模式有几种常见的方式,每种方式都有其特点和适用场景。以下是一些常用的实现方法:

  1. 懒汉式(双重检查锁定)
  2. 饿汉式(静态内部类)
  3. 枚举(Enum)

1. 懒汉式(双重检查锁定)

代码示例

1public class Singleton {
2    private volatile static Singleton instance; // 使用 volatile 确保可见性
3
4    private Singleton() {
5        // 防止反射攻击
6        if (instance != null) {
7            throw new IllegalStateException("Singleton instance already created!");
8        }
9    }
10
11    public static Singleton getInstance() {
12        if (instance == null) { // 第一次检查
13            synchronized (Singleton.class) { // 加锁
14                if (instance == null) { // 第二次检查
15                    instance = new Singleton(); // 实例化
16                }
17            }
18        }
19        return instance;
20    }
21}

解释

  • 双重检查锁定(Double-Checked Locking):首先进行一次空检查,如果 instance 为空,则进入同步块。这样可以避免每次调用 getInstance() 方法时都要进行同步,提高了性能。
  • volatile:使用 volatile 关键字来确保多线程环境下的可见性。当 instance 被初始化后,其他线程可以立即看到这个变化。
  • 构造函数私有化:确保外部无法通过构造函数直接创建实例。
  • 防止反射攻击:在构造函数中加入检查,防止通过反射绕过构造函数私有化创建实例。

2. 饿汉式(静态内部类)

代码示例

1public class Singleton {
2    private Singleton() {}
3
4    private static class SingletonHolder {
5        private static final Singleton INSTANCE = new Singleton();
6    }
7
8    public static Singleton getInstance() {
9        return SingletonHolder.INSTANCE;
10    }
11}

解释

  • 静态内部类:使用静态内部类来持有单例实例。静态内部类只会被加载一次,因此确保了实例的唯一性。
  • 延迟加载:虽然静态内部类是静态的,但它并不会在类加载时立即初始化。只有当第一次访问 Singleton.getInstance() 时,静态内部类才会被加载和初始化。
  • 线程安全:静态内部类的初始化是由 JVM 保证线程安全的,因此这种方式也是线程安全的。

3. 枚举(Enum)

代码示例

1public enum Singleton {
2    INSTANCE;
3
4    public void someMethod() {
5        // 实现单例的方法
6    }
7}

解释

  • 枚举:使用枚举来实现单例模式是最简单也是最安全的方式。枚举类型天然支持线程安全单例模式
  • 简单易用:枚举提供了一种简单的方式,可以直接在枚举类型中定义单例对象,并在枚举中实现单例的方法。

合理化的使用建议

  1. 选择合适的实现方式

    • 如果需要延迟初始化,可以选择 懒汉式(双重检查锁定) 或 饿汉式(静态内部类)
    • 如果需要最简单的实现方式,可以选择 枚举(Enum)
  2. 性能考虑

    • 如果性能是一个重要因素,建议使用 静态内部类 或 枚举,因为它们在初始化时只发生一次,之后每次获取实例都非常快。
    • 如果需要延迟初始化并且性能要求不高,可以选择 双重检查锁定
  3. 代码清晰性

    • 选择最简单明了的方式,使代码易于理解和维护。枚举方式通常是首选,因为它既简单又安全

实际开发过程中的注意点

  • 线程安全
    在多线程环境中,确保单例模式的线程安全性非常重要。使用 volatile 和双重检查锁定可以有效防止多线程并发创建多个实例的问题。

  • 反射攻击
    即使构造函数是私有的,仍然可以通过反射机制创建对象。在构造函数中添加检查可以防止这种情况。

  • 序列化问题
    如果单例对象需要支持序列化,需要重写 readResolve() 方法来确保反序列化时返回单例实例。

    1public class Singleton implements Serializable {
    2    private static final long serialVersionUID = 1L;
    3
    4    private Singleton() {}
    5
    6    private static class SingletonHolder {
    7        private static final Singleton INSTANCE = new Singleton();
    8    }
    9
    10    public static Singleton getInstance() {
    11        return SingletonHolder.INSTANCE;
    12    }
    13
    14    protected Object readResolve() {
    15        return getInstance();
    16    }
    17}
  • 防止 JVM 重排序
    在使用双重检查锁定时,volatile 关键字不仅可以确保可见性,还可以防止 JVM 的指令重排序,从而确保实例化过程的原子性。

通过上述讨论,我们可以看到,在 Java 中实现线程安全单例模式有多种方法,每种方法都有其适用场景。

选择最合适的方法取决于具体的需求和上下文。

在实际开发中,确保单例模式的线程安全性是非常重要的,同时也要考虑性能、代码清晰性和维护性。


http://www.ppmy.cn/ops/107593.html

相关文章

Python办公自动化案例

文章目录 系列文章办公自动化案例案例1:批量重命名文件案例2:Excel数据自动筛选案例3:PDF文件合并案例4:批量发送电子邮件案例5:日程安排提醒案例6:CSV文件数据统计案例7:Word文档生成案例8&…

2024年AMC10美国数学竞赛倒计时两个月:吃透1250道真题和知识点(持续)

根据通知,2024年AMC10美国数学竞赛的报名还有两周,正式比赛还有两个月就要开始了。计划参赛的孩子们要记好时间,认真备考,最后冲刺再提高成绩。 那么如何备考2024年AMC10美国数学竞赛呢?做真题,吃透真题和…

[HCTF 2018]WarmUp1

启动靶机后看到这样的界面 我们F12可以看到它提示source.php 访问后我们就可以看到后端代码 <?phphighlight_file(__FILE__); #用于显示当前文件的源代码。class emmm{public static function checkFile(&$page){$whitelist ["source">"source.p…

python学习

Python 基础&#xff08;一&#xff09;&#xff1a;入门必备知识_python1基础学习-CSDN博客 Python 基础&#xff08;二&#xff09;&#xff1a;基本语句 Python 基础&#xff08;三&#xff09;&#xff1a;我是一个数字 Python 基础&#xff08;四&#xff09;&#xff…

SpringBoot教程(十五) | SpringBoot集成RabbitMq(消息丢失、消息重复、消息顺序、消息顺序)

SpringBoot教程&#xff08;十五&#xff09; | SpringBoot集成RabbitMq&#xff08;消息丢失、消息重复、消息顺序、消息顺序&#xff09; RabbitMQ常见问题解决方案问题一&#xff1a;消息丢失的解决方案&#xff08;1&#xff09;生成者丢失消息丢失的情景解决方案1&#xf…

一些硬件知识(十八)

两个信号PIN之间串接电阻的作用&#xff1a; 1.阻抗匹配 2.吸收反射 3.防止程序异常导致两个IO都是输出的时候短路 尤其针对下图中的信号&#xff1a; 清理穿越机电机中的灰尘&#xff0c;可以用密封胶泥的办法&#xff1a; 一定要小心垫片的掉落&#xff1a; 20块左右的快充充…

【Linux】进程状态(RSDT 阻塞 僵尸 孤儿)

目录 进程状态 进程状态的查看 R和S 运行状态 T/t 暂停状态 kill命令 D &#xff08;disk sleep&#xff09;状态、 Z 状态&#xff08;僵尸状态&#xff09; 孤儿状态 运行状态 阻塞状态 进程状态 一个进程通常有三种状态 ◉ 就绪状态&#xff08;Ready&#xff0…

day43(9/4)——k8s

一、前期准备 1、配置主机映射 [rootk8s-master ~]# vim /etc/hosts 192.168.8.168 k8s-master 192.168.8.176 k8s-node1 192.168.8.177 k8s-node2 [rootk8s-master ~]# ping k8s-master 2、配置yum源 [rootk8s-master yum.repos.d]# vim kubernetes.repo [kubernetes…

MARK图像处理与计算机视觉基础,经典以及最近发展

图像处理与计算机视觉的经典书籍 *************************************************************************************************************** 本文章的源作者是 杨晓冬 &#xff08;个人邮箱&#xff1a;xdyang.ustcgmail.com&#xff09;。 原文的链接是 htt…

潜聊django认证模块

潜聊django认证模块 一、什么是Django认证模块&#xff1f; Django的认证模块&#xff08;Authentication System&#xff09;主要用于认证&#xff08;确认用户的身份&#xff09;和授权&#xff08;决定用户可以做什么&#xff09;。这个模块帮助你管理用户的登录、注册、权…

hutool 集合相关交集、差集

开发过程中会遇到集合之间的对比之类的需求&#xff0c;之前经常会自己写个工具类来实现&#xff0c;目前hutool可以帮助我们解决很多问题&#xff0c;接下来我们就来实践下。 相关jar包 <dependency><groupId>cn.hutool</groupId><artifactId>hutool…

GD - EmbeddedBuilder - 给已有工程换MCU

文章目录 GD - EmbeddedBuilder - 给已有工程换MCU概述不行的重现 笔记工程的.gdc文件内容中有MCU型号可以改 给已有工程换MCU的使用场景END GD - EmbeddedBuilder - 给已有工程换MCU 概述 一个现存的EmbeddedBuilder的工程&#xff0c;想换个MCU配置做实验&#xff0c;又不想…

echarts 水平柱图 科技风

var category [{ name: "管控", value: 2500 }, { name: "集中式", value: 8000 }, { name: "纳管", value: 3000 }, { name: "纳管", value: 3000 }, { name: "纳管", value: 3000 } ]; // 类别 var total 10000; // 数据…

线性因子模型 - 概率PCA和因子分析篇

序言 在探索数据科学与机器学习的浩瀚领域中&#xff0c;深度学习作为一股不可小觑的力量&#xff0c;正以前所未有的方式重塑着我们对数据处理与知识发现的理解。在这一宏大的框架下&#xff0c;概率主成分分析&#xff08; Probabilistic PCA, pPCA \text{Probabilistic PCA…

XGBoost算法-上

简单解释一下xgboost这个模型 xg是一个非常强大&#xff0c;非常受欢迎的机器学习模型&#xff0c;其中最大的特色就是boosting&#xff08;改进、推进&#xff09;&#xff0c;怎么改进呢&#xff1f;就是xgboost这个算法&#xff0c;它会先建立一颗简单的决策树&#xff0c;…

WordPress安装指南:主题、插件和最佳实践

WordPress是世界上最流行的内容管理系统&#xff08;CMS&#xff09;&#xff0c;因其易用性和灵活性而备受欢迎。本文将指导您完成WordPress的安装过程&#xff0c;介绍一些常用的主题和插件&#xff0c;并分享一些重要的注意事项。 1. WordPress安装 步骤1&#xff1a;准备…

常见监督学习算法学习总结。

目录 一、K临近算法 二、决策树 三、多层感知器 四、伯努利贝叶斯算法 五、高斯贝叶斯 一、K临近算法 K 临近算法&#xff08;K-Nearest Neighbors&#xff0c;简称 KNN&#xff09;是一种监督学习算法&#xff0c;用于分类和回归任务。 它通过计算样本之间的距离来进行…

鸿蒙自动化发布测试版本app

创建API客户端 API客户端是AppGallery Connect用于管理用户访问AppGallery Connect API的身份凭据&#xff0c;您可以给不同角色创建不同的API客户端&#xff0c;使不同角色可以访问对应权限的AppGallery Connect API。在访问某个API前&#xff0c;必须创建有权访问该API的API…

iOS 中,用户点击一个按钮到响应的全部流程

在 iOS 中&#xff0c;当用户点击一个按钮&#xff08;或其他 UI 控件&#xff09;时&#xff0c;会触发一系列复杂的操作流程&#xff0c;从硬件到软件&#xff0c;再到应用层的事件处理。以下是从用户点击一个按钮到应用响应的完整流程&#xff1a; 硬件层&#xff1a;触摸事…

libvncclient编写多线程qt的VNC客户端

概述 使用qt和libvncclient编写vnc的客户端程序&#xff0c;多线程读写&#xff0c;拒绝卡顿。qt环境&#xff1a;5.15.3libvncclient&#xff1a;0.9.14下载地址&#xff1a;https://github.com/LibVNC/libvncserver/releases 编译libvncclient 打开CMakeList文件&#xff…