单例模式详解:如何优雅地实现线程安全的单例

news2024/11/16 7:12:38

一、什么是单例模式?

单例模式是一种常用的设计模式,目的就是确保某个类在程序中只有一个实例,并且提供一个全局访问入口。通过这个模式,我们能够保证全局共享同一个对象实例,避免了多次实例化同一个对象,节省内存,提升性能。

二、单例模式的优点

  1. 节省内存与计算资源
    单例模式确保只会创建一个对象实例,避免了多次创建同一个对象,减少了内存和计算资源的消耗。

  2. 方便管理与控制
    对象的管理变得更加集中,能够方便地对单例对象进行控制与管理,特别适用于全局共享的资源,比如数据库连接池、日志记录器等。

  3. 线程安全
    单例模式常常与多线程环境配合使用,确保多线程情况下只能有一个实例创建,避免了线程安全问题。

三、线程安全的单例模式实现

在多线程环境下,我们通常需要保证单例实例的创建是线程安全的。下面是使用“双重检查锁”实现线程安全的单例模式代码:

public class Singleton {
    private static volatile Singleton singleton;

    // 私有构造函数,防止外部通过new来创建实例
    private Singleton() {
    }

    // 获取单例对象的方法
    public static Singleton getInstance() {
        // 第一层检查,避免不必要的同步
        if (singleton == null) {
            synchronized (Singleton.class) {
                // 第二层检查,确保只有第一个线程创建实例
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

四、为什么需要“双重检查锁模式”?

在上述代码中,我们使用了双重检查锁(Double-Checked Locking)来确保线程安全并优化性能。理解为什么需要两次 if 判断,首先要了解以下几个方面:

1. 第一个 if 判断

  • 目的是避免不必要的同步。当第一个线程进入时,实例还未创建,它会进入 synchronized 块去创建实例。而如果实例已经被创建,后续的线程就无需进入同步块,直接返回已有实例,从而提高性能。

2. 第二个 if 判断

  • 保证单例唯一性。假设有两个线程 A 和 B 都同时执行了第一个 if 判断,并且都发现 singleton == null。此时,线程 A 获得了锁并创建了实例,而线程 B 在等待锁的过程中,也会经过第一次 if 判断,并进入同步块,但此时线程 A 已经创建了实例。
  • 如果没有第二个 if 判断,线程 B 也会重新创建一个新的实例,这就破坏了单例模式的初衷。

五、为什么需要 volatile 关键字?

在这段代码中,我们给 singleton 加上了 volatile 关键字。volatile 在这里的作用是保证线程之间的可见性,防止指令重排序带来的问题。

1. singleton = new Singleton() 不是原子操作

在 JVM 中,执行 singleton = new Singleton() 至少包含以下三步:

  • singleton 分配内存空间
  • 调用 Singleton 的构造函数初始化对象
  • singleton 引用指向分配的内存空间

在多线程环境下,JVM 可能会对这三步指令进行重排序。假如指令重排序导致 singleton 被赋值为空(第3步)但对象还未初始化完成(第2步未完成),这会导致其他线程误以为 singleton 已经初始化完成,从而访问一个未完全初始化的对象。

2. volatile 保证可见性

volatile 可以避免指令重排序,从而确保在一个线程中对 singleton 的修改对其他线程立即可见,确保单例实例只会被初始化一次。

六、总结

单例模式在保证程序中对象的唯一性和全局访问的同时,能够有效节省资源、提高效率。在多线程环境下,我们通过“双重检查锁模式”来确保单例的线程安全,并通过 volatile 关键字保证对象的可见性和防止指令重排序。

通过以上的学习,你应该已经理解了如何优雅地实现一个线程安全的单例模式。这个实现方式不仅性能优越,而且能够在多线程环境中有效防止并发问题,是许多高并发系统中不可或缺的设计模式之一。

🌟 关注我的CSDN博客,收获更多技术干货! 🌟

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

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

相关文章

Chromium 中sqlite数据库操作演示c++

本文主要演示sqlite数据库 增删改查创建数据库以及数据库表的基本操作,仅供学习参考。 一、sqlite数据库操作类封装: sql\database.h sql\database.cc // Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-sty…

pycharm分支提交操作

一、Pycharm拉取Git远程仓库代码 1、点击VCS > Get from Version Control 2、输入git的url,选择自己的项目路径 3、点击Clone,就拉取成功了 默认签出分支为main 选择develop签出即可进行开发工作 二、创建分支(非必要可以不使用&#xf…

河道无人机雷达测流监测系统由哪几部分组成?

在现代水利管理中,河道无人机雷达监测系统正逐渐成为一种重要的工具,为河道的安全和管理提供了强大的技术支持。那么,这个先进的监测系统究竟由哪几部分组成呢? 河道无人机雷达监测系统工作原理 雷达传感器通过发射电磁波或激光束…

使用pycrawlers下载huggingface报错list index out of range问题解决

使用pycrawlers下载huggingface失败 错误:list index out of range 问题描述:当批量下载整个huggingface仓库的时候会报错,并且没有报错提示 分析:应该是哪个链接请求结果不存在数据 结果:当下载仓库存在文件夹下面只…

清华大学提出基于ESKF的松耦合里程计RINO:一种具有非迭代估计的精确、稳健的雷达惯性里程计

Abstract 精确的定位和建图对于实现自动驾驶车辆的自主导航至关重要。然而,当GNSS信号失效或在极端天气条件(例如雾、雨和雪)下,车体运动估计仍然面临重大挑战。近年来,扫描雷达因其较强的穿透能力成为一种有效的解决…

微信小程序之路由跳转传数据及接收

跳转并传id或者对象 1.home/index.wxml <!--点击goto方法 将spu_id传过去--> <view class"item" bind:tap"goto" data-id"{{item.spu_id}}"> 结果: 2.home/index.js goto(event){// 路由跳转页面,并把id传传过去//获取商品idlet i…

tensorflow案例6--基于VGG16的猫狗识别(准确率99.8%+),以及tqdm、train_on_batch的简介

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 前言 本次还是学习API和如何搭建神经网络为主&#xff0c;这一次用VGG16去对猫狗分类&#xff0c;效果还是很好的&#xff0c;达到了99.8% 文章目录 1、tqdm…

海康大华宇视视频平台EasyCVR私有化视频平台服务器选购主要参数有哪些?

在构建现代服务器和视频监控系统时&#xff0c;选择合适的硬件配置和关键技术是确保系统性能和稳定性的基础。服务器选购涉及到多个关键参数&#xff0c;这些参数直接影响到服务器的处理能力、数据存储、网络通信等多个方面。 同时&#xff0c;随着视频监控技术的发展&#xf…

Redisson的可重入锁

初始状态&#xff1a; 表示系统或资源在没有线程持有锁的情况下的状态&#xff0c;任何线程都可以尝试获取锁。 线程 1 获得锁&#xff1a; 线程 1 首次获取了锁并进入受保护的代码区域。 线程 1 再次请求锁&#xff1a; 在持有锁的情况下&#xff0c;线程 1 再次请求锁&a…

Java-01 深入浅出 MyBatis - MyBatis 概念 ORM映射关系 常见ORM 详细发展历史

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 大数据篇正在更新&#xff01;https://blog.csdn.net/w776341482/category_12713819.html 目前已经更新到了&#xff1a; MyBatis&#xff…

C语言第13节:指针(3)

1. 回调函数 回调函数的基本思想是&#xff0c;将函数指针作为参数传递给另一个函数&#xff0c;并在需要时通过这个函数指针调用对应的函数。这种方式允许一个函数对执行的内容进行控制&#xff0c;而不需要知道具体的实现细节。 回调函数在以下场景中尤为有用&#xff1a; …

Tensorflow基本概念

简介&#xff1a;本文从Graph讲到Session&#xff0c;同时讲解了tf.constant创建tensor的用法和variable需要初始化的知识点&#xff0c;可以给你打好一个学习Tensorflow的基础。本文都是基于TensorFlow1.14.0的版本下运行。 本专栏将会系统的讲解TensorFlow在1.14.0版本下的各…

【包教包会】CocosCreator3.x框架——带翻页特效的场景切换

一、效果演示 二、如何获取 1、https://gitee.com/szrpf/TurnPage 2 2、解压&#xff0c;导入cocos creator&#xff08;版本3.8.2&#xff09;&#xff0c;可以直接运行Demo演示 三、算法思路 1、单场景 页面预制体 通过loadScene来切换页面&#xff0c;无法实现页面特效…

【MySQL 保姆级教学】事务的自动提交和手动提交(重点)--上(13)

目录 1. 什么是事务&#xff1f;2. 事务的版本支持3. 事务提交的方式3.1 事务提交方式的分类3.2 演示的准备的工作3.2.1 创建表3.2.2 MySQL的服务端和客户端3.2.3 调低事务的隔离级别 4. 手动提交4.1 手动提交的命令说明4.2 示例一4.3 示例二4.4 示例三4.5 示例四 5. 自动提交5…

几何合理的分片段感知的3D分子生成 FragGen - 评测

FragGen 来源于 2024 年 3 月 25 日 预印本的文章&#xff0c;文章题目是 Deep Geometry Handling and Fragment-wise Molecular 3D Graph Generation&#xff0c; 作者是 Odin Zhang&#xff0c;侯廷军&#xff0c;浙江大学药学院。FragGen 是一个基于分子片段的 3D 分子生成模…

数据结构笔记(其八)--一般树的存储及其遍历

1.知识总览 一般的树会有多个孩子&#xff0c;所以存储结构也会与二叉树略有不同。 一般树的遍历。 2.双亲表示法 双亲表示法&#xff0c;也是父亲表示法&#xff0c;即每个节点中都存储了其父节点的地址信息。 特性&#xff1a;可以轻易地找到父节点&#xff0c;但寻找孩子节…

Linux系统Centos设置开机默认root用户

目录 一. 教程 二. 部分第三方工具配置也无效 一. 教程 使用 Linux 安装Centos系统的小伙伴大概都知道&#xff0c;我们进入系统后&#xff0c;通常都是自己设置的普通用户身份&#xff0c;而不是 root 超级管理员用户&#xff0c;导致我们在操作文件夹时往往爆出没有权限&am…

医院信息化与智能化系统(21)

医院信息化与智能化系统(21) 这里只描述对应过程&#xff0c;和可能遇到的问题及解决办法以及对应的参考链接&#xff0c;并不会直接每一步详细配置 如果你想通过文字描述或代码画流程图&#xff0c;可以试试PlantUML&#xff0c;告诉GPT你的文件结构&#xff0c;让他给你对应…

【论文阅读】利用SEM二维图像表征黏土矿物三维结构

导言 在油气储层研究中&#xff0c;黏土矿物对流体流动的影响需要在微观尺度上理解&#xff0c;但传统的二维SEM图像难以完整地表征三维孔隙结构。常规的三维成像技术如FIB-SEM&#xff08;聚焦离子束扫描电子显微镜&#xff09;虽然可以获取高精度的3D图像&#xff0c;但成本…

Yocto - 使用Yocto开发嵌入式Linux系统_13 创建定制层

Creating Custom Layers 除了使用社区或供应商提供的现有图层外&#xff0c;我们还将在本章中学习如何为我们的产品创建图层。此外&#xff0c;我们还将了解如何创建机器定义和分布&#xff0c;并从中获益&#xff0c;从而更好地组织我们的源代码。 In addition to using exist…