热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入解析CAS机制:全面替代传统锁的底层原理与应用

本文深入探讨了CAS(Compare-and-Swap)机制,分析了其作为传统锁的替代方案在并发控制中的优势与原理。CAS通过原子操作确保数据的一致性,避免了传统锁带来的性能瓶颈和死锁问题。文章详细解析了CAS的工作机制,并结合实际应用场景,展示了其在高并发环境下的高效性和可靠性。


作者:景小财


资料来源: https://WWW .建校.com/p/e 674 EE 68F D3F


1、为什么要用锁?锁定-是为了解决因同时操作而导致的脏读取、数据不一致的问题。


2、锁实现的基本原理 2.1,卷


在Java编程语言中,线程可以访问共享变量。 为了确保共享变量的准确和一致更新,线程必须通过独占锁定分别获取该变量。 Java语言提供了volatile,有时比锁定更有用。


volatile在多处理器开发中保证了共享变量的“可视性”。 可见性表示当一个线程更改共享变量时,另一个线程可以读取该更改的值。




结论:如果volatile变量修饰符使用得当,则不会引起线程上下文的切换和调度,因此成本低于同步的使用和执行。


2.2、同步化


同步通过锁定机制实现同步。


首先,让我们来看看使用同步的同步基础。 Java中的所有对象都可以用作锁定。


具体表现为以下三种形式。


在常规同步方法中,锁定是当前的实例对象。 对于静态同步方法,锁定是当前类的Class对象。 对于同步方法块,锁定是在已同步括号中设置的对象。 当线程尝试访问同步代码块时,必须首先获取锁定,然后在退出或引发异常时解除锁定。


2.2.1同步化的实现原理


同步基于监视器实现同步。


Monitor从两个方面支持线程之间的同步。


独占执行联合1、Java使用对象锁定(使用同步获取对象锁定),保证在共享数据集上运行的线程的独占执行。


使用通知/通知全部/等待方法来协调不同线程之间的工作。


3、Class和Object都与Monitor有关。




线程进入同步方法。 线程必须获得Monitor锁定才能继续执行关键代码。 成功获取锁定后,您将成为该监视者的目标所有者。 在任一时刻,监视者对象只属于一个活动线程(The Owner ),具有监视者对象的线程为wait ) )进入等待集,同时释放监视锁,处于等待状态其他线程调用notify ()/notifyAll ) )接口,以在执行wait ) )之后的代码之前,启动等待集中需要重新获取监视锁的线程。 同步方法执行完毕。 线程结束关键部分并解除监视锁定。


参考文献: https://www.IBM.com /开发者工作/cn/Java/j-lo-synchronized


2.2.2同步的具体实现


1、同步代码块使用monitorenter、monitorexit指令明确实现。


2、同步方法使用ACC_SYNCHRONIZED标志隐式实现。


让我们来看看具体的实现示例。


公共类同步测试{


公共同步语音方法1 (


system.out.println('Helloworld!' );


}


公共语音方法2 ()


已同步(this ) {


system.out.println('Helloworld!' );


}


}


}


javap编译后的字节码如下。




监视器中心


每个对象都有一个监视器,一个监视器只能由一个线程拥有。 线程执行monitorenter指令时,会尝试获取对应对象的monitor。 取得规则如下。


如果monitor的条目数为0,则线程可以进入monitor,如果将monitor的条目数设置为1,则线程为monitor的所有者。 如果当前线程已经具有此monitor,并且只需要重新输入,则在monitor中的输入数将增加1,因此使用synchronized关键字实现的锁定是可重新输入的锁定。 如果monitor已经由其他线程拥有,则当前线程将处于阻塞状态,直到monitor的条目数变为0,然后重新尝试获取monitor。 监视器


拥有的是

相应对象的monitor的线程才能执行monitorexit指令。每执行一次该指令monitor进入数减1,当进入数为0时当前线程释放monitor,此时其他阻塞的线程将可以尝试获取该monitor。

2.2.3 锁存放的位置

锁标记存放在Java对象头的Mark Word中。

2.2.3 synchronized的锁优化

JavaSE1.6为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”。

在JavaSE1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。

锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。

偏向锁:

无锁竞争的情况下为了减少锁竞争的资源开销,引入偏向锁。

轻量级锁:

轻量级锁所适应的场景是线程交替执行同步块的情况。

锁粗化(Lock Coarsening): 也就是减少不必要的紧连在一起的unlock,lock操作,将多个连续的锁扩展成一个范围更大的锁。

锁消除(Lock Elimination): 锁削除是指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行削除。

适应性自旋(Adaptive Spinning): 自适应意味着自旋的时间不再固定了,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次成功,进而它将允许自旋等待持续相对更长的时间,比如100个循环。另一方面,如果对于某个锁,自旋很少成功获得过,那在以后要获取这个锁时将可能省略掉自旋过程,以避免浪费处理器资源。

2.2.4 锁的优缺点对比

2.3、CAS

CAS,在Java并发应用中通常指CompareAndSwap或CompareAndSet,即比较并交换。

1、CAS是一个原子操作,它比较一个内存位置的值并且只有相等时修改这个内存位置的值为新的值,保证了新的值总是基于最新的信息计算的,如果有其他线程在这期间修改了这个值则CAS失败。CAS返回是否成功或者内存位置原来的值用于判断是否CAS成功。

2、JVM中的CAS操作是利用了处理器提供的CMPXCHG指令实现的。

优点:

竞争不大的时候系统开销小。

缺点:

循环时间长开销大。ABA问题。只能保证一个共享变量的原子操作。3、Java中的锁实现

3.1、队列同步器(AQS)

队列同步器AbstractQueuedSynchronizer(以下简称同步器),是用来构建锁或者其他同步组件的基础框架。

3.1.1、它使用了一个int成员变量表示同步状态。

3.1.2、通过内置的FIFO双向队列来完成获取锁线程的排队工作。

同步器包含两个节点类型的应用,一个指向头节点,一个指向尾节点,未获取到锁的线程会创建节点线程安全(compareAndSetTail)的加入队列尾部。同步队列遵循FIFO,首节点是获取同步状态成功的节点。

未获取到锁的线程将创建一个节点,设置到尾节点。如下图所示:

首节点的线程在释放锁时,将会唤醒后继节点。而后继节点将会在获取锁成功时将自己设置为首节点。如下图所示:

3.1.3、独占式/共享式锁获取

独占式:有且只有一个线程能获取到锁,如:ReentrantLock。

共享式:可以多个线程同时获取到锁,如:CountDownLatch

独占式

每个节点自旋观察自己的前一节点是不是Header节点,如果是,就去尝试获取锁。

独占式锁获取流程:

共享式:

共享式与独占式的区别:

共享锁获取流程:

4、锁的使用用例

4.1、ConcurrentHashMap的实现原理及使用(1.7)

ConcurrentHashMap类图

ConcurrentHashMap数据结构

结论:ConcurrentHashMap使用的锁分段技术。首先将数据分成一段一段地存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。


推荐阅读
  • 微软Exchange服务器遭遇2022年版“千年虫”漏洞
    微软Exchange服务器在新年伊始遭遇了一个类似于‘千年虫’的日期处理漏洞,导致邮件传输受阻。该问题主要影响配置了FIP-FS恶意软件引擎的Exchange 2016和2019版本。 ... [详细]
  • 本文详细探讨了Java中的24种设计模式及其应用,并介绍了七大面向对象设计原则。通过创建型、结构型和行为型模式的分类,帮助开发者更好地理解和应用这些模式,提升代码质量和可维护性。 ... [详细]
  • 本文详细介绍了 Dockerfile 的编写方法及其在网络配置中的应用,涵盖基础指令、镜像构建与发布流程,并深入探讨了 Docker 的默认网络、容器互联及自定义网络的实现。 ... [详细]
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • 并发编程:深入理解设计原理与优化
    本文探讨了并发编程中的关键设计原则,特别是Java内存模型(JMM)的happens-before规则及其对多线程编程的影响。文章详细介绍了DCL双重检查锁定模式的问题及解决方案,并总结了不同处理器和内存模型之间的关系,旨在为程序员提供更深入的理解和最佳实践。 ... [详细]
  • 深入解析 Apache Shiro 安全框架架构
    本文详细介绍了 Apache Shiro,一个强大且灵活的开源安全框架。Shiro 专注于简化身份验证、授权、会话管理和加密等复杂的安全操作,使开发者能够更轻松地保护应用程序。其核心目标是提供易于使用和理解的API,同时确保高度的安全性和灵活性。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ... [详细]
  • 本文详细探讨了Netty中Future及其子类的设计与实现,包括其在并发编程中的作用和具体应用场景。我们将介绍Future的继承体系、关键方法的实现细节,并讨论如何通过监听器和回调机制来处理异步任务的结果。 ... [详细]
  • Hadoop入门与核心组件详解
    本文详细介绍了Hadoop的基础知识及其核心组件,包括HDFS、MapReduce和YARN。通过本文,读者可以全面了解Hadoop的生态系统及应用场景。 ... [详细]
  • MySQL索引详解与优化
    本文深入探讨了MySQL中的索引机制,包括索引的基本概念、优势与劣势、分类及其实现原理,并详细介绍了索引的使用场景和优化技巧。通过具体示例,帮助读者更好地理解和应用索引以提升数据库性能。 ... [详细]
  • 本文探讨了领域驱动设计(DDD)的核心概念、应用场景及其实现方式,详细介绍了其在企业级软件开发中的优势和挑战。通过对比事务脚本与领域模型,展示了DDD如何提升系统的可维护性和扩展性。 ... [详细]
  • 本文探讨了 Spring Boot 应用程序在不同配置下支持的最大并发连接数,重点分析了内置服务器(如 Tomcat、Jetty 和 Undertow)的默认设置及其对性能的影响。 ... [详细]
author-avatar
阿加芬散阿加芬散
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有