单例模式的理解

目录

    • 单例模式
      • 1.饿汉式(线程安全)
      • 2.懒汉式(通过synchronized修饰获取实例的方法保证线程安全)
      • 3.双重校验锁的方式实现单例模式
      • 4.静态内部类方式实现单例模式【推荐】

单例模式

1.饿汉式(线程安全)

package 并发的例子.单例模式;
// 饿汉式单例模式(天然线程安全,但不支持懒加载)
public class Singleton1 {// 1. 静态成员变量:在类加载阶段(JVM层面)就完成实例化//    - static保证全局唯一一份,类加载时由JVM保证线程安全(仅初始化一次)//    - final修饰防止被意外修改,确保实例不可变private static final Singleton1 INSTANCE = new Singleton1();// 2. 私有构造方法:禁止外部通过new创建实例,保证单例唯一性private Singleton1() {}// 3. 公开静态方法:提供全局访问点,直接返回已初始化的实例public static Singleton1 getInstance() {return INSTANCE;}// 测试:验证多次获取的是否为同一实例public static void main(String[] args) {Singleton1 instance1 = Singleton1.getInstance();Singleton1 instance2 = Singleton1.getInstance();Singleton1 instance3 = Singleton1.getInstance();// 输出均为true,证明所有引用指向同一个实例System.out.println(instance1 == instance2);System.out.println(instance2 == instance3);System.out.println(instance1 == instance3);}
}

2.懒汉式(通过synchronized修饰获取实例的方法保证线程安全)

package 并发的例子.单例模式;
// 懒汉式(通过synchronized修饰获取实例的方法保证线程安全,但由于整个方法加锁,效率不高性能略差)
public class Singleton2 {// 定义实例对象引用(仅声明,未创建实例,实现延迟初始化的基础)private static Singleton2 instance;// 私有构造方法,防止其他类通过new关键字创建实例,确保单例唯一性private Singleton2() {}// 公开的静态方法,用于获取单例实例// 使用synchronized修饰方法:保证多线程环境下,同一时间只有一个线程能进入方法,避免创建多个实例public static synchronized Singleton2 getInstance() {// 懒加载(延迟加载):只有当首次调用getInstance()时,才会创建实例对象,节省初始化资源if (instance == null) {instance = new Singleton2();}return instance;}public static void main(String[] args) {// 测试单例模式:多次获取实例,验证是否为同一对象Singleton2 s1 = Singleton2.getInstance();Singleton2 s2 = Singleton2.getInstance();Singleton2 s3 = Singleton2.getInstance();// 通过hashCode判断是否为同一对象(同一对象的hashCode相同)System.out.println(s1.hashCode());System.out.println(s2.hashCode());System.out.println(s3.hashCode());}
}

3.双重校验锁的方式实现单例模式

package 并发的例子.单例模式;// 用双重校验锁的方式实现单例模式
// 简单说就是:保证整个程序里只有这一个类的对象,而且线程安全、用到时才创建、效率还不错
public class Singleton3 {// 存单例对象的地方,整个程序就这一个// volatile关键字有两个超能力:// 1. 防止"指令插队":创建对象的时候步骤必须是【分配内存→初始化对象→给instance赋值】,不能乱序// 2. 保证"看得见":一个线程把对象创建好赋值给instance了,其他线程马上能看到,不会因为缓存犯傻private static volatile Singleton3 instance;// 把构造方法藏起来,不让外面用new创建对象// 这样就只能通过我们写的getInstance方法来拿对象,保证只有一个private Singleton3() {}// 对外提供的拿单例对象的方法,全局就这一个入口public static Singleton3 getInstance() {// 第一次检查:先快速看看有没有对象// 要是已经有了,直接返回,不用走后面的复杂流程,省时间if (instance == null) {// 加锁排队:多个线程同时到这的时候,只能一个一个来// 锁的是整个类(Singleton3.class),保证全局就这一把锁synchronized (Singleton3.class) {// 第二次检查:进了锁之后再看一眼// 防止多个线程都通过第一次检查后,进来重复创建对象// 要是不检查,线程1创建完,线程2进来又创建,就不是单例了if (instance == null) {// 真正创建对象的地方// 要是没加volatile,可能出现"对象还没初始化好,就把半成品给instance"的情况// 加了volatile就保证步骤是【分配内存→初始化对象→给instance赋值】,稳稳的instance = new Singleton3();}}}// 返回单例对象,不管哪个线程来拿,都是同一个return instance;}
}

4.静态内部类方式实现单例模式【推荐】

package 并发的例子.单例模式;
// 静态内部类方式实现单例模式【推荐】
// 特点:线程安全(依托类加载机制) + 懒加载(真正用到实例时才加载) + 简洁高效
public class Singleton4 {// 1. 私有化构造方法//    作用:禁止外部通过 new Singleton4() 创建对象,确保对象只能通过 getInstance() 获取private Singleton4() {}// 2. 静态内部类:SingletonHolder//    特点://    - 静态内部类不会随着外部类加载而加载,属于「懒加载」private static class SingletonHolder {// 3. 静态内部类中定义单例对象//    - final 保证实例不可变,一旦赋值无法修改//    - 类加载时创建实例,由 JVM 保证线程安全(多线程下不会重复创建)private static final Singleton4 INSTANCE = new Singleton4();}// 4. 对外提供获取单例的方法public static Singleton4 getInstance() {// 调用此方法时,才会触发 SingletonHolder 的类加载// 类加载过程中,JVM 会创建 INSTANCE,且保证全局唯一、线程安全return SingletonHolder.INSTANCE;}public static void main(String[] args) {Singleton4 s1 = Singleton4.getInstance();Singleton4 s2 = Singleton4.getInstance();Singleton4 s3 = Singleton4.getInstance();// 通过hashCode判断是否为同一对象(同一对象的hashCode相同)System.out.println(s1.hashCode());System.out.println(s2.hashCode());System.out.println(s3.hashCode());}
}
1.为什么用静态内部类?
静态内部类 SingletonHolder 是 “懒汉”:外部类 Singleton4 加载时,它不会跟着加载,真正调用 getInstance() 时才会加载,实现 “用的时候再创建”(懒加载)。
2.线程安全怎么保证?
JVM 加载类时,会保证 “同一类全局只加载一次”,且加载过程是线程安全的(多线程同时调用 getInstance()SingletonHolder 也只会加载一次)。
因此 INSTANCE 只会创建一次,天然线程安全。
3.对比其他单例的优势
比 “饿汉式” 懒:饿汉式类加载时就创建实例,静态内部类做到了 “用的时候才创建”。
比 “懒汉式(同步方法)” 高效:无需手动加锁,依托 JVM 类加载机制保证线程安全,性能更好。
4.适合场景
需要 懒加载(延迟初始化),且希望 线程安全、代码简洁 的场景。
推荐作为日常开发中 “单例模式” 的首选方案,兼顾性能和安全性。

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

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

相关文章

NLP---IF-IDF案例分析

一案例 - 红楼梦1首先准备语料库http://www.dxsxs.com这个网址去下载2 任务一:拆分提取import os import redef split_hongloumeng():# 1. 配置路径(关键:根据实际文件位置修改) # 脚本所在文件夹(自动获取&#xff0…

LaTeX(排版系统)Texlive(环境)Vscode(编辑器)环境配置与安装

LaTeX、Texlive 和 Vscode 三者之间的关系,可以把它们理解成语言、工具链和编辑器的配合关系。 1.下载Texlive 华为镜像网站下载 小编这边下载的是texlive2025.iso最新版的,下载什么版本看自己需求,只要下载后缀未.iso的即可。为避免错误&am…

【深入浅出STM32(1)】 GPIO 深度解析:引脚特性、工作模式、速度选型及上下拉电阻详解

GPIO 深度解析:引脚特性、工作模式、速度选型及上下拉电阻详解一、GPIO概述二、GPIO的工作模式1、简述(1)4种输入模式(2)4种输出模式(3)4种最大输出速度2、引脚速度(1)输…

第1节 大模型分布式推理基础与技术体系

前言:为什么分布式推理是大模型时代的核心能力? 当我们谈论大模型时,往往首先想到的是训练阶段的千亿参数、千卡集群和数月的训练周期。但对于商业落地而言,推理阶段的技术挑战可能比训练更复杂。 2025年,某头部AI公司推出的130B参数模型在单机推理时面临两个选择:要么…

《软件工程导论》实验报告一 软件工程文档

目 录 一、实验目的 二、实验环境 三、实验内容与步骤 四、实验心得 一、实验目的 1. 理解软件工程的基本概念,熟悉软件,软件生命周期,软件生存周期过程和软件生命周期各阶段的定义和内容。 2. 了解软件工程文档的类别、内容及撰写软件工…

基于elk实现分布式日志

1.基本介绍 1.1 什么是分布式日志 在分布式应用中,日志被分散在储存不同的设备上。如果你管理数十上百台服务器,你还在使用依次登录每台机器的传统方法查阅日志。这样是不是感觉很繁琐和效率低下。所以我们使用集中化的日志管理,分布式日志…

多模态RAG赛题实战之策略优化--Datawhale AI夏令营

科大讯飞AI大赛(多模态RAG方向) - Datawhale 项目流程图 1、升级数据解析方案:从 fitz 到 MinerU PyMuPDF(fitz)是基于规则的方式提取pdf里面的数据;MinerU是基于深度学习模型通过把PDF内的页面看成是图片…

09--解密栈与队列:数据结构核心原理

1. 栈 1.1. 栈的简介 栈 是一种 特殊的线性表,具有数据 先进后出 特点。 注意: stack本身 不支持迭代器操作 主要原因是因为stack不支持数据的随机访问,必须保证数据先进后出的特点。stack在CPP库中实现为一种 容器适配器 所谓容器适配器&a…

打造专属 React 脚手架:从 0 到 1 开发 CLI 工具

前言: 在前端开发中,重复搭建项目环境是个低效的事儿。要是团队技术栈固定(比如 React AntD Zustand TS ),每次从零开始配路由、状态管理、UI 组件,既耗时又容易出错。这时候,自定义 CLI 脚手架 就派上…

Python day43

浙大疏锦行 Python day43 import torch import numpy as np import pandas as pd import torchvision import torchvision.transforms as transforms import torch.nn as nn import torch.optim as optim import torch.nn.functional as F from torch.utils.data import Da…

python基于Hadoop的超市数据分析系统

前端开发框架:vue.js 数据库 mysql 版本不限 后端语言框架支持: 1 java(SSM/springboot)-idea/eclipse 2.NodejsVue.js -vscode 3.python(flask/django)–pycharm/vscode 4.php(thinkphp/laravel)-hbuilderx 数据库工具:Navicat/SQLyog等都可以 摘要&…

如何用 COLMAP 制作 Blender 格式的数据集

如何用 COLMAP 制作 Blender 格式的数据集并划分出 transforms_train.json、transforms_val.json 和 transforms_test.json。 一、什么是 Blender 格式数据集? Blender 格式数据集是 Nerf 和 Nerfstudio 常用的输入格式,其核心是包含了相机内外参的 JSON 文件,一般命名为:…

[GESP202309 六级] 2023年9月GESP C++六级上机题题解,附带讲解视频!

本文为GESP 2023年9月 六级的上机题目详细题解和讲解视频,觉得有帮助或者写的不错可以点个赞。 题目一讲解视频 GESP2023年9月六级上机题一题目二讲解视频 题目一:小羊买饮料 B3873 [GESP202309 六级] 小杨买饮料 - 洛谷 题目大意: 现在超市一共有n种饮料&#…

linux 操作ppt

目录 方法1:用 libreoffice 打开PPT文件 播放脚本: 方法2:用 python-pptx 创建和编辑PPT 方法3:其他方法 在Linux中,可以使用Python通过python-pptx库来创建和编辑PPT文件,但直接播放PPT文件需要借助其…

元数据管理与数据治理平台:Apache Atlas 基本搜索 Basic Search

文中内容仅限技术学习与代码实践参考,市场存在不确定性,技术分析需谨慎验证,不构成任何投资建议。 Apache Atlas 框架是一套可扩展的核心基础治理服务,使企业能够有效、高效地满足 Hadoop 中的合规性要求,并支持与整个…

LangChain4J-(1)-Hello World

一、LangChain4J是什么? LangChain4J 是一个专为 Java 生态系统设计的开源框架,用于简化与大语言模型(LLM,如 OpenAI 的 GPT 系列、Google 的 Gemini、Anthropic 的 Claude 等)的集成和交互。它借鉴了 Python 生态中 L…

HTTPS应用层协议-中间攻击人

HTTPS应用层协议-中间攻击人 • Man-in-the-MiddleAttack,简称“MITM 攻击” 确实,在方案 2/3/4 中,客户端获取到公钥 S 之后,对客户端形成的对称秘钥 X 用服务端给客户端的公钥 S 进行加密,中间人即使窃取到了数据&am…

利用 Makefile 高效启动 VIVADO 软件:深入解析与实践

利用 Makefile 高效启动 VIVADO 软件:深入解析与实践 系列文章目录 1、VMware Workstation Pro安装指南:详细步骤与配置选项说明 2、VMware 下 Ubuntu 操作系统下载与安装指南 3.基于 Ubuntu 的 Linux 系统中 Vivado 2020.1 下载安装教程 文章目录利用 …

[前端算法]排序算法

默认情况下,sort() 会将元素转换为字符串,然后按照 Unicode 编码的顺序进行排序: const fruits [apple, banana, cherry, date]; fruits.sort(); console.log(fruits); // 输出: ["apple", "banana", "cherry"…

C#标签批量打印程序开发

C#标签批量打印程序开发(集成Bartender解决方案)一、系统架构设计 1. 核心模块划分 public class LabelPrintingSystem {private IDataLoader _dataLoader; // 数据加载器private ITemplateEngine _templateEngine; // 模板引擎private IPrintControl…