【高并发趣事三】——双重检查锁定与延迟初始化

简介: 【高并发趣事三】——双重检查锁定与延迟初始化

一、引言


在JAVA多线程程序中,有时候需要采用延迟初始化来降低初始化类和创建对象的 开销。双重检查锁定就是延迟 初始化技术。


二、双重检查锁定的由来


在jav进程中,有时候可能 需要推迟一些高开销对象的初始化操作,并且只有在使用这些对象时才进行初始化,此时,程序员可能会采用延迟初始化。还有一个经典的使用场景就是单利模式下的,为了提高性能 ,采用双重检查锁定模式。但是在使用的过程中我们需要一些技巧,否则很容易出现问题。


20200723171256576.png

上面代码在多线程环境下,我们很容易出现问题,所以改进代码。


20200723171355498.png

由于对getInstance()方法做了同步处理,synchronized将导致性能的开销。如果 getInstance()方法被多个线程频繁调用,将会导致程序执行性能下降。因此,为了提高性能,聪明的程序员想出了一个很好技巧: 双重检查锁定(DCL).继续优化代码


20200723171638684.png

这段代码看起来非常完美,但是这是一个错误的优化,在线程执行到第4行代码的时候,代码渠道instance不为null时,instance引用对象有可能还没有完成初始化。


三、 问题的源


在第七行代码,创建对象时候,我们可以将这一行代码进行分解为下面的伪代码。


20200723172017976.png


这就是我们JVM类加载的三个过程的抽象,如果我们非常熟悉类加载过程,我们应该会很清楚上面的三行伪代码的意思。


为什么将第七行代码拆开来说看呢,因为这三个过程可能会发生指令重排,发生指令重排后可能的执行顺序为:

20200723172439876.png


发生这样的指令重排完全符合规则,因为它重排以后保证了 单线程内程序的执行规则。

这样的执行顺序,在多线程的环境下,就会出现问题了,线程B将看到一个没有初始化的对象。


2020072317273381.png


四、解决方案


通过上面分析,我们知道了问题的原因是指令重排造成的,具体来说是上面的2和3发生重排导致,所以 我们解决问题的关键就是怎么解决重排的问题,下面两个思路


1、不允许2和3重排

2、允许2和3发生重排序,但是不允许其他线程看到这个重排序


基于volatile的解决方案


20200723173217943.png


当声明对象的引用为volatile后,上面的伪代码中的2和3指令之间的重排序在多环境中将会被禁止。


基于类的初始化的解决方案


JVM在类的初始化阶段, 会执行累点 初始化。在执行类的初始化期间,JVM会获取一个锁。这个锁可以同步多个线程对同一个类的初始化。基于这个特性,可以实现另一种线程安全的初始化方案。

20200723174937769.png

假设两个线程并发执行getInstance()方法,下面是执行示意图:


20200723175038959.png



这个方案的实质是,允许2和3重排序,但不允许非构造线程(B线程) 看到这个重排序。

目录
相关文章
|
存储 Java 编译器
【高并发趣事二】——JMM及程序中的幽灵
【高并发趣事二】——JMM及程序中的幽灵
96 0
【高并发趣事二】——JMM及程序中的幽灵
【高并发趣事一】——Amdahl(阿姆达尔定律)与Gustafson(古斯塔夫森定律)
【高并发趣事一】——Amdahl(阿姆达尔定律)与Gustafson(古斯塔夫森定律)
384 0
【高并发趣事一】——Amdahl(阿姆达尔定律)与Gustafson(古斯塔夫森定律)
|
SQL 开发框架 缓存
TPL Dataflow组件应对高并发,低延迟要求
2C互联网业务增长,单机多核的共享内存模式带来的排障问题、编程困难;随着多核时代和分布式系统的到来,共享模型已经不太适合并发编程,因此actor-based模型又重新受到了人们的重视。
TPL Dataflow组件应对高并发,低延迟要求
|
7月前
|
消息中间件 Java Linux
2024年最全BATJ真题突击:Java基础+JVM+分布式高并发+网络编程+Linux(1),2024年最新意外的惊喜
2024年最全BATJ真题突击:Java基础+JVM+分布式高并发+网络编程+Linux(1),2024年最新意外的惊喜
|
6月前
|
缓存 NoSQL Java
Java高并发实战:利用线程池和Redis实现高效数据入库
Java高并发实战:利用线程池和Redis实现高效数据入库
535 0
|
4月前
|
监控 算法 Java
企业应用面临高并发等挑战,优化Java后台系统性能至关重要
随着互联网技术的发展,企业应用面临高并发等挑战,优化Java后台系统性能至关重要。本文提供三大技巧:1)优化JVM,如选用合适版本(如OpenJDK 11)、调整参数(如使用G1垃圾收集器)及监控性能;2)优化代码与算法,减少对象创建、合理使用集合及采用高效算法(如快速排序);3)数据库优化,包括索引、查询及分页策略改进,全面提升系统效能。
56 0
|
6月前
|
存储 NoSQL Java
探索Java分布式锁:在高并发环境下的同步访问实现与优化
【6月更文挑战第30天】Java分布式锁在高并发下确保数据一致性,通过Redis的SETNX、ZooKeeper的临时节点、数据库操作等方式实现。优化策略包括锁超时重试、续期、公平性及性能提升,关键在于平衡同步与效率,适应大规模分布式系统的需求。
202 1
|
5月前
|
算法 Java 调度
高并发架构设计三大利器:缓存、限流和降级问题之使用Java代码实现令牌桶算法问题如何解决
高并发架构设计三大利器:缓存、限流和降级问题之使用Java代码实现令牌桶算法问题如何解决
|
5月前
|
监控 网络协议 Java
Java面试题:解释Java NIO与BIO的区别,以及NIO的优势和应用场景。如何在高并发应用中实现NIO?
Java面试题:解释Java NIO与BIO的区别,以及NIO的优势和应用场景。如何在高并发应用中实现NIO?
86 0
|
5月前
|
设计模式 安全 NoSQL
Java面试题:设计一个线程安全的单例模式,并解释其内存占用和垃圾回收机制;使用生产者消费者模式实现一个并发安全的队列;设计一个支持高并发的分布式锁
Java面试题:设计一个线程安全的单例模式,并解释其内存占用和垃圾回收机制;使用生产者消费者模式实现一个并发安全的队列;设计一个支持高并发的分布式锁
74 0