单例模式-懒汉式(双重检验)

本文探讨了懒汉式单例模式在多线程环境下的问题,通过双重检测机制和volatile关键字的引入,解决了线程安全与性能之间的矛盾,确保对象的正确初始化与内存可见性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上章节我们在懒汉式的单例模式上解决了多线程安全的问题,但解决问题的同时,新的问题也随之而来。

上节问题:

1、在静态方法(static)上添加关键字(synchronized同步锁),就是相当于在类上加锁,锁的范围大,损耗性能。

2、加锁、解锁过程消耗资源。

那么,我们该如何解决呢?

 1 public class lazyDoubleCheckSingleton {
 2 
 3   private static lazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;
 4 
 5   private lazyDoubleCheckSingleton() {
 6 
 7   }
 8   public static lazyDoubleCheckSingleton getInstance() {
 9     if (lazyDoubleCheckSingleton == null) {
10       synchronized (lazyDoubleCheckSingleton.class){
11         if (lazyDoubleCheckSingleton == null){
12           lazyDoubleCheckSingleton = new lazyDoubleCheckSingleton();
//1、分配内存给这个对象
//2、初始化对象
//3、设置
lazyDoubleCheckSingleton,指向刚分配的内存地址
13  } 14  } 15  } 16 return lazyDoubleCheckSingleton; 17  } 18 }

此种方法就是懒汉模式的双重检测式,把锁加在方法里面,只有空的话才会加锁,不为空的话,直接return lazyDoubleCheckSingleton,大大节省了开销,但是这段代码,还存在着两大隐患,分别是9行、12行,首先在9行判断了是否为空,有可能是不为空的,他虽然不为空,但是是在12行还没完成初始化的情况下,12行的代码虽然是一行,确实经历了三个步骤,注释上2和3的顺序可能调换,进行重排序,也就是先指向内存地址,但是还没初始化对象。下面给大家开一个图。

这是单线程,介入多线程呢?

看吧,就出问题了呢,想要解决这个问题,有两种解决方案

1、不允许2、3进行重排序,加入了volatile关键字.
2、允许一个线程进行重排序,但不允许另外线程看到他的重排序。
那么我们看看用第一种方案是怎么解决的呢?
/**
 * Created by sww_6 on 2019/4/10.
 * 双重检查
 * 1、不允许2、3进行重排序,加入了volatile关键字.
 * 2、允许一个线程进行重排序,但不允许另外线程看到他的重排序。
 * 划重点:
 * 1、在多线程的时候,cpu有共享内存。
 * 2、加入了volatile关键字之后,所有线程都能看到共享内存的最新状态,保证内存可见性。
 * <p>
 * 怎么保持内存一致性?
 * 用volatile修饰的共享变量,在进行写操作的时候,会多出来很多汇编代码,起到两个作用。
 * 1、是将当前处理器缓存行的数据写回到系统内存,写回内存的操作,会使在其他cpu里缓存了该内存的数据无效,其他cpu缓存的数据无效了,就会从共享内存同步数据。保证了内存的可见性。
 * 主要使用的是缓存一致性协议
 * <p>
 * 优点:
 * 既兼顾了性能,又兼顾了线程安全。
 */
public class lazyDoubleCheckSingleton {

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

好了,我们下期见!

 

转载于:https://www.cnblogs.com/shenwen/p/10686682.html

资源下载链接为: https://pan.quark.cn/s/1bfadf00ae14 “STC片机电压测量”是一个以STC系列片机为基础的电压检测应用案,它涵盖了硬件电路设计、软件编程以及数据处理等核心知识点。STC片机凭借其低功耗、高性价比和丰富的I/O接口,在电子工程领域得到了广泛应用。 STC是Specialized Technology Corporation的缩写,该公司的片机基于8051内核,具备内部振荡器、高速运算能力、ISP(在系统编程)和IAP(在应用编程)功能,非常适合用于各种嵌入控制系统。 在源代码方面,“浅雪”风格的代码通常简洁易懂,非常适合初学者学习。其中,“main.c”文件是程序的入口,包含了电压测量的核心逻辑;“STARTUP.A51”是启动代码,负责初始化片机的硬件环境;“电压测量_uvopt.bak”和“电压测量_uvproj.bak”可能是Keil编译器的配置文件备份,用于设置编译选项和项目配置。 对于3S锂电池电压测量,3S锂电池由三节锂离子电池串联而成,标称电压为11.1V。测量时需要考虑电池的串联特性,通过分压电路将高电压转换为片机可接受的范围,并实时监控,防止过充或过放,以确保电池的安全和寿命。 在电压测量电路设计中,“电压测量.lnp”文件可能包含电路布局信息,而“.hex”文件是编译后的机器码,用于烧录到片机中。电路中通常会使用ADC(模拟数字转换器)将模拟电压信号转换为数字信号供片机处理。 在软件编程方面,“StringData.h”文件可能包含程序中使用的字符串常量和数据结构定义。处理电压数据时,可能涉及浮点数运算,需要了解STC片机对浮点数的支持情况,以及如何高效地存储和显示电压值。 用户界面方面,“电压测量.uvgui.kidd”可能是用户界面的配置文件,用于显示测量结果。在嵌入系统中,用
### Java 中懒汉单例模式的实现 懒汉单例模式是一种延迟初始化的方,在第一次调用 `getInstance` 方法时才创建实对象。这种方可以节省资源,因为只有当真正需要该对象的时候才会被创建。 为了防止多线程环境下可能出现的问题,通常会在方法上加上同步锁来确保线程安全: ```java public class Singleton { private static Singleton instance; private Singleton() {} public synchronized static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } ``` 然而上述方虽然实现了线程安全性,但是每次获取实都会进行加锁操作,影响性能效率[^1]。 通过引入 `volatile` 关键字可以在不牺牲太多性能的情况下解决这个问题。`volatile` 可以保证变量的可见性和有序性,从而避免指令重排序带来的潜在风险。具体来说就是让多个线程能够看到共享变量最新的值,并且禁止编译器和处理器对读取或写入 volatile 字段的操作做任何优化处理。 改进后的懒汉单例模式如下所示: ```java 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; } } ``` 这里采用了双重检测锁定(Double-Checked Locking),即在进入临界区前后各增加了一次判空语句。这样做不仅提高了程序执行速度,同时也保障了线程间的正确协作[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值