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

单例模式的理解

目录

    • 单例模式
      • 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.lryc.cn/news/619997.html

相关文章:

  • Spring Security 前后端分离场景下的会话并发管理
  • C语言:指针(4)
  • 【2025】Datawhale AI夏令营-多模态RAG-Task3笔记-解决方案进阶
  • 蓝蜂网关在雄安新区物联网建设中的关键应用
  • 补环境基础(四) Hook插件
  • Spring Boot项目调用第三方接口的三种方式比较
  • 当img占不满div时,图片居中显示,两侧加当前图片模糊效果
  • 如何记录日常笔记?
  • 【Linux学习|黑马笔记|Day3】root用户、查看权限控制信息、chmod、chown、快捷键、软件安装、systemctl、软连接、日期与时区
  • 语音交互像聊天:声网RTC技术给AI客服加温度
  • 基于 MybatisPlus 将百度天气数据存储至 PostgreSQL 数据库的实践
  • 开发避坑指南(25):MySQL不支持带有limit语句的子查询的解决方案
  • Java研学-RabbitMQ(六)
  • 算法题详细解析 + 代码 + 注释
  • 在 uniapp 里使用 unocss,vue3 + vite 项目
  • 数据结构初阶(12)排序算法—插入排序(插入、希尔)(动图演示)
  • 智驾系统架构解析
  • 常用机器学习公开数据集大全
  • [系统架构设计师]系统架构基础知识(一)
  • [系统架构设计师]信息安全技术基础知识(三)
  • DataOceanAI Dolphin(ffmpeg音频转化教程) 多语言(中国方言)语音识别系统部署与应用指南
  • 最新去水印小程序系统 前端+后端全套源码 多套模版 免授权
  • TF-IDF实战——《红楼梦》文本分析
  • 商品分类拖拽排序设计
  • 用 Qt C++ 从零打通“前端界面 → 后端接口”的数据交互
  • Redis的基础命令
  • 图像分类-动手学计算机视觉10
  • RabbitMQ:Windows版本安装部署
  • 高防CDN和高防IP的各自优势
  • Vue项目生产环境性能优化实战指南