双重检验锁方式实现单例模式
创作时间:
作者:
@小白创作中心
双重检验锁方式实现单例模式
引用
CSDN
1.
https://blog.csdn.net/Flying_Fish_roe/article/details/142908772
Java中的单例模式
单例模式(Singleton Pattern)是一种设计模式,它确保一个类在整个程序运行期间只有一个实例,并提供一个全局的访问点。单例模式常用于需要在多个地方共享同一个资源或者需要控制对象创建的场景,例如数据库连接、线程池、配置文件管理等。
单例模式的基本要点
- 唯一性 :确保类只有一个实例。
- 全局访问点 :提供一个全局的访问方式,让其他类能够获取该实例。
- 延迟初始化 :有时需要在第一次使用时才创建实例,避免浪费资源。
单例模式的实现方式
单例模式有多种实现方式,主要包括以下几种:
- 饿汉式(Eager Initialization) :在类加载时就创建实例。
- 懒汉式(Lazy Initialization) :在第一次需要使用实例时才创建它。
- 双重检查锁定(Double-Checked Locking) :一种优化的懒汉式实现,结合了延迟加载和线程安全的特点。
- 静态内部类 :利用类加载机制实现线程安全的单例模式。
- 枚举单例 :利用枚举实现单例,天生线程安全并且防止反射和序列化漏洞。
双重检查锁定(Double-Checked Locking)方式实现单例模式
双重检查锁定是一种优化的单例模式实现方式,目的是在确保线程安全的同时,减少不必要的同步开销。这个模式的核心思想是:通过检查实例是否已经被创建来避免每次获取实例时都进行同步操作,从而提高性能。
实现步骤
- 声明私有静态变量 :持有单例实例的引用,使用
volatile关键字修饰以确保内存可见性。 - 私有构造方法 :确保类不能被外部实例化。
- 提供公有的静态方法 :用于获取单例实例,内部实现双重检查锁定。
以下是一个基于双重检查锁定实现的单例模式的示例:
public class Singleton {
// 使用volatile确保多线程环境下的可见性和防止指令重排序
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是为了防止指令重排序(Instruction Reordering)。这是因为实例化对象的过程并非原子操作,通常包含以下步骤:- 为
Singleton分配内存。 - 调用
Singleton的构造函数,初始化对象。 - 将
instance引用指向分配的内存地址。
- 为
- 如果不使用
volatile,步骤2和步骤3可能会被重排序,从而导致在多线程环境中,一个线程看到instance已经指向某个内存地址,但对象还未初始化完成,这可能会引发难以调试的错误。
- 第一次检查(
if (instance == null)) :
- 目的是避免不必要的同步操作。如果
instance已经被创建,直接返回即可,避免进入同步块,提高性能。
- 同步块(
synchronized) :
- 用于确保在多线程环境下,只有一个线程能够进入同步块并创建实例,从而防止多个线程同时创建多个实例。
- 第二次检查(
if (instance == null)) :
- 在同步块内进行第二次检查,以确保在进入同步块后实例依然未被创建。如果没有第二次检查,即使多个线程已经通过了第一次检查,仍可能同时创建多个实例。
优点
- 线程安全 :通过同步和双重检查,确保了线程安全。
- 高效 :相比每次都同步的懒汉式实现,双重检查锁定只在第一次创建实例时才进行同步,大大减少了同步的开销。
- 延迟加载 :实例仅在第一次调用
getInstance()方法时创建,节省资源。
缺点
- 复杂性 :相对于其他实现方式,双重检查锁定的实现较为复杂,需要了解
volatile关键字的使用和指令重排序的概念。 - 依赖于JVM :早期的Java版本(Java 5之前)中,JVM对
volatile的实现存在问题,可能导致双重检查锁定失效。虽然这一问题在Java 5及以后版本已经解决,但仍需要了解历史背景。
常见问题与解决
- 指令重排序问题 :通过使用
volatile关键字解决,确保对象的初始化过程不会被重排序。 - 反射和序列化漏洞 :即使使用双重检查锁定,反射和序列化仍可能破坏单例模式。可以通过以下方法进一步保护单例:
- 防止反射 :在构造函数中检查
instance是否已被创建,如果已被创建,则抛出异常。 - 防止序列化 :实现
readResolve方法,确保反序列化时返回同一个实例。
private Singleton() {
if (instance != null) {
throw new RuntimeException("Use getInstance() method to create");
}
}
protected Singleton readResolve() {
return getInstance();
}
总结
双重检查锁定是一种优化的单例模式实现方式,通过减少不必要的同步操作,提高了程序的执行效率,同时也能确保线程安全和延迟加载。尽管它在某些情况下实现较为复杂,但在现代Java环境中(Java 5及以后),这是一个非常有效且常用的单例模式实现方式。
单例模式在实际开发中广泛应用于配置管理、数据库连接管理、日志系统等场景,通过合理的设计和实现,可以确保系统在多线程环境中的正确性和高效性。理解双重检查锁定的机制及其优缺点,有助于开发者编写更健壮的Java程序。
热门推荐
盘点李世民反悔的两桩婚事,一桩被骂不厚道,另一桩被骂羞耻
大唐弘化公主和亲事迹研究
大葱田间浇水管理技巧:确保健康生长的关键
王者荣耀典韦出装攻略,三板斧秒人六神装搭配思路
苏轼笔下的“舳舻千里”:从水上军阵到文学意象
如何正确佩戴耳机:提升音质与舒适度的全方位指南
捕捉稻城亚丁之美:深度摄影攻略与实用拍照技巧分享
福建最有名的七大特产美食,每一道都是极品,看看你都吃过吗?
辽源去四川成都一日游路线:行程规划及里程指南
肾功能不全用利尿剂
大数据与会计专业融合:如何通过数据分析提升财务决策?
成都摄影攻略:宽窄巷子、杜甫草堂、青城山三大景点拍摄指南
从用户到体验,如何开始搭建「用户行为分析」来深化业务改良【构建篇】
成都巨型刘亦菲雕塑成新晋网红打卡胜地
春晚名场面:赵雅芝叶童再现经典,王菲时隔7年再登台,金晨意外成笑点
揭秘:布偶猫背后的传奇女性——安贝可
布偶猫PK褴褛猫:谁是猫咪界颜值担当?
布偶猫健康大揭秘:10种常见疾病的预防与治疗
人类祖先“露西”还有这本事!
徐冰《蜻蜓之眼》:监控时代的隐私危机
如果细究公子扶苏之死,事情就变得有趣起来
大秦名公子扶苏的外貌探秘
清远到张家界自驾游路线推荐:全程900公里,需时9小时
千亿投资!三亚规划52个重点交通项目
希腊特洛伊古城遗址留给世界的震撼
巴黎先贤祠,雨果、大仲马、居里夫人葬在这里
拉瓦锡:化学革命的奠基者与历史的牺牲品
直播平台开发技术揭秘:音视频处理与互动功能
波兰军费突破4%:北约标准升级下的经济与安全困境
深度游福建 走这七条线就对了