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

单例模式及线程安全的实践

🌟 欢迎来到 我的博客! 🌈

💡 探索未知, 分享知识 !💫

在这里插入图片描述

本文目录

    • 引言
    • 基本的单例模式长啥样?
    • 怎样才能线程安全?
      • **懒汉模式** ( 双 重 检 查 )
    • 🎉总结🎉


引言

单例模式是个挺实用的设计模式,它的要点就是确保一个类只有一个实例,并且提供一个访问这个实例的全局点。这种模式在你需要控制资源或者保持全局状态的时候特别有帮助。但在多线程的情况下,实现这个模式就需要一些技巧,以确保安全和效率。下面咱们就一起看看怎么实现一个既安全又高效的单例模式。

基本的单例模式长啥样?

先来看一个最简单的单例模式示例:

public class Singleton {private static Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
这个例子简单明了,但在多线程的场景下就可能出问题了。比如,如果两个线程同时检查到 instance为空,那么它们就都会创建一个实例,这就不符合单例模式的初衷了。

PS :
就像图示中,这样就创建了多个实例对象

在这里插入图片描述

饿汉式单例

这段代码实现的是所谓的饿汉式单例模式。在这种模式下,单例的实例在类被加载到JVM时就立即初始化了。这种方式简单直接,因为它依靠JVM类加载机制保证实例的唯一性,同时也无需担心多线程问题,因为类加载过程是线程安全的。

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

优点是实现简单,类加载时完成初始化,避免了线程同步问题。

缺点是如果这个类比较大,而且在程序启动时就加载,但长时间不使用,会导致资源浪费。


怎样才能线程安全?

可以给获取实例的方法加个锁:
public class Singleton {private static Singleton instance;private Singleton() {}public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}

这种方法虽然简单,但每次调用 getInstance() 都会加锁,可能会拖慢速度。

懒汉模式 ( 双 重 检 查 )

这个方法更聪明点,只在需要时加锁:

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

这样,只有在第一次创建实例时才会同步,提高了效率。而且,用了 volatile 关键字来确保变量的可见性。

这种方式的关键在于`instance`字段的`volatile`关键字和`synchronized`块。`volatile`确保当`instance`变量被初始化成单例实例时,多个线程正确地处理`instance`变量,`synchronized`块则确保在实例未初始化前只有一个线程能进入初始化代码区。

优点 : 只有在实际使用时才会创建实例。并且通过双重检查锁定机制减少了锁的粒度,提高了效率。
缺点 : 实现复杂,需要多重检查以确保线程安全。但我个人更推荐使用这种方法

🎉总结🎉

🎈在多线程环境下实现一个既安全又高效的单例模式需要一些技巧。根据你的需求和具体情况,你可以选择加锁、双重检查或者静态内部类的方法。如果你在乎性能,双重检查和静态内部类是不错的选择,因为它们在保证了线程安全的同时,也考虑到了效率。

相关文章:

  • 20个常用的Python脚本
  • Java开发与配置用到的各类中间件官网
  • MongoDB聚合运算符:$dateFromString
  • SQL中如何添加数据
  • 【kubernetes】关于k8s集群的pod控制器
  • 华为ce12800交换机m-lag(V-STP模式)配置举例
  • OD_2024_C卷_200分_9、园区参观路径【JAVA】【动态规划】
  • 【SpringCloud微服务实战02】Ribbon 负载均衡
  • 是什么原因影响到服务器的稳定性
  • L1阶段题解方法总结
  • 游戏中主流的护甲计算
  • 微信聊天助手
  • HarmonyOS系统开发基础环境搭建
  • 年轻人退休或失业?藏在身边的冷门暴利行业,2024适合年轻人轻资产创业项目!
  • 基于SpringBoot的招聘网站
  • 《Java编程思想》读书笔记-对象导论
  • Git的一些常用操作
  • javascript数组去重/查找/插入/删除
  • Laravel 实践之路: 数据库迁移与数据填充
  • npx命令介绍
  • orm2 中文文档 3.1 模型属性
  • Redis字符串类型内部编码剖析
  • spring security oauth2 password授权模式
  • 力扣(LeetCode)22
  • 入职第二天:使用koa搭建node server是种怎样的体验
  • 扫描识别控件Dynamic Web TWAIN v12.2发布,改进SSL证书
  • 没有任何编程基础可以直接学习python语言吗?学会后能够做什么? ...
  • 树莓派用上kodexplorer也能玩成私有网盘
  • ​用户画像从0到100的构建思路
  • # 数论-逆元
  • $.ajax()
  • (+4)2.2UML建模图
  • (1)虚拟机的安装与使用,linux系统安装
  • (30)数组元素和与数字和的绝对差
  • (4)通过调用hadoop的java api实现本地文件上传到hadoop文件系统上
  • (超详细)语音信号处理之特征提取
  • (代码示例)使用setTimeout来延迟加载JS脚本文件
  • (二)斐波那契Fabonacci函数
  • (二刷)代码随想录第16天|104.二叉树的最大深度 559.n叉树的最大深度● 111.二叉树的最小深度● 222.完全二叉树的节点个数
  • (附源码)计算机毕业设计SSM疫情下的学生出入管理系统
  • (一)【Jmeter】JDK及Jmeter的安装部署及简单配置
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • (转)mysql使用Navicat 导出和导入数据库
  • ***汇编语言 实验16 编写包含多个功能子程序的中断例程
  • .NET Framework .NET Core与 .NET 的区别
  • .NET 实现 NTFS 文件系统的硬链接 mklink /J(Junction)
  • .Net的DataSet直接与SQL2005交互
  • .net反编译的九款神器
  • .NET企业级应用架构设计系列之技术选型
  • .NET企业级应用架构设计系列之结尾篇
  • .Net小白的大学四年,内含面经
  • @WebServiceClient注解,wsdlLocation 可配置
  • [ C++ ] STL_vector -- 迭代器失效问题
  • [ 蓝桥杯Web真题 ]-布局切换
  • []指针