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

CAS原理分析

一、锁机制


常用的锁机制有两种:

1、悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。悲观锁的实现,往往依靠底层提供的锁机制;悲观锁会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。

2、乐观锁:假设不会发生并发冲突,每次不加锁而是假设没有冲突而去完成某项操作,只在提交操作时检查是否违反数据完整性。如果因为冲突失败就重试,直到成功为止。乐观锁大多是基于数据版本记录机制实现。为数据增加一个版本标识,比如在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。 

乐观锁的缺点是不能解决脏读的问题。

在实际生产环境里边,如果并发量不大且不允许脏读,可以使用悲观锁解决并发问题;但如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以我们就要选择乐观锁定的方法.

锁机制存在以下问题:

(1)在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。
(2)一个线程持有锁会导致其它所有需要此锁的线程挂起。
(3)如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险。


二、CAS 操作


JDK 5之前Java语言是靠synchronized关键字保证同步的,这是一种独占锁,也是是悲观锁。java.util.concurrent(J.U.C)种提供的atomic包中的类,使用的是乐观锁,用到的机制就是CAS,CAS(Compare and Swap)有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

现代的CPU提供了特殊的指令,允许算法执行读-修改-写操作,而无需害怕其他线程同时修改变量,因为如果其他线程修改变量,那么CAS会检测它(并失败),算法可以对该操作重新计算。而 compareAndSet() 就用这些代替了锁定。

以AtomicInteger为例,研究在没有锁的情况下是如何做到数据正确性的。

[java]  view plain  copy
  1. public class AtomicInteger extends Number implements java.io.Serializable {  
  2.       
  3.     private volatile int value;  
  4.   
  5.   
  6.       
  7.     public final int get() {  
  8.         return value;  
  9.     }  
  10.       
  11.     public final int getAndIncrement() {  
  12.         for (;;) {  
  13.             int current = get();  
  14.             int next = current + 1;  
  15.             if (compareAndSet(current, next))  
  16.                 return current;  
  17.         }  
  18.     }  
  19.       
  20.     public final boolean compareAndSet(int expect, int update) {  
  21.         return unsafe.compareAndSwapInt(this, valueOffset, expect, update);  
  22.     }  


字段value需要借助volatile原语,保证线程间的数据是可见的(共享的)。这样在获取变量的值的时候才能直接读取。然后来看看++i是怎么做到的。getAndIncrement采用了CAS操作,每次从内存中读取数据然后将此数据和+1后的结果进行CAS操作,如果成功就返回结果,否则重试直到成功为止。而compareAndSet利用JNI来完成CPU指令的操作。

[java]  view plain  copy
  1. public final boolean compareAndSet(int expect, int update) {     
  2.     return unsafe.compareAndSwapInt(this, valueOffset, expect, update);  
  3.  }  


整体的过程就是这样子的,利用CPU的CAS指令,同时借助JNI来完成Java的非阻塞算法。其它原子操作都是利用类似的特性完成的。
而整个J.U.C都是建立在CAS之上的,因此对于synchronized阻塞算法,J.U.C在性能上有了很大的提升。

CAS第一个问题是会导致“ABA问题”。
aba实际上是乐观锁无法解决脏数据读取的一种体现。CAS算法实现一个重要前提需要取出内存中某时刻的数据,而在下时刻比较并替换,那么在这个时间差类会导致数据的变化。比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且two进行了一些操作变成了B,然后two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后one操作成功。尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。如果链表的头在变化了两次后恢复了原值,但是不代表链表就没有变化。因此AtomicStampedReference/AtomicMarkableReference就很有用了。


AtomicMarkableReference 类描述的一个<Object,Boolean>的对,可以原子的修改Object或者Boolean的值,这种数据结构在一些缓存或者状态描述中比较有用。这种结构在单个或者同时修改Object/Boolean的时候能够有效的提高吞吐量。 


AtomicStampedReference 类维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。对比AtomicMarkableReference 类的<Object,Boolean>,AtomicStampedReference 维护的是一种类似<Object,int>的数据结构,其实就是对对象(引用)的一个并发计数(标记版本戳stamp)。但是与AtomicInteger 不同的是,此数据结构可以携带一个对象引用(Object),并且能够对此对象和计数同时进行原子操作。

[java]  view plain  copy
  1.   

相关文章:

  • Java中静态跟非静态的区别总结
  • Spring IOC原理解读 面试必读
  • spring ioc原理(看完后大家可以自己写一个spring)
  • 非可抢占式和抢占式进程调度的区别是什么?
  • 【数据结构】ArrayList原理及实现学习总结
  • Java集合:HashMap源码剖析
  • 从原则、方案、策略及难点阐述分库分表
  • IDEA,WebStorm ,pyCharms 2018注册码 (2018jetbrain所有产品都可以)
  • css如何让div显示在最上层
  • XMind制作思维导图——添加子标题
  • JAVA中的Random()函数,获取随机数
  • FastJson对于JSON格式字符串、JSON对象及JavaBean之间的相互转换
  • linux wget安装以及使用
  • Servlet中response、request乱码问题解决
  • IntelliJ IDEA设置类代码模板自定义(注释)
  • [分享]iOS开发 - 实现UITableView Plain SectionView和table不停留一起滑动
  • 【干货分享】SpringCloud微服务架构分布式组件如何共享session对象
  • Android组件 - 收藏集 - 掘金
  • angular学习第一篇-----环境搭建
  • CEF与代理
  • chrome扩展demo1-小时钟
  • css属性的继承、初识值、计算值、当前值、应用值
  • Django 博客开发教程 8 - 博客文章详情页
  • MySQL-事务管理(基础)
  • nodejs调试方法
  • python学习笔记-类对象的信息
  • Python学习之路13-记分
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • React组件设计模式(一)
  • Spark学习笔记之相关记录
  • vue自定义指令实现v-tap插件
  • 阿里云Kubernetes容器服务上体验Knative
  • 基于Android乐音识别(2)
  • 技术胖1-4季视频复习— (看视频笔记)
  • 聊聊redis的数据结构的应用
  • 聊聊spring cloud的LoadBalancerAutoConfiguration
  • 前端路由实现-history
  • 微信开源mars源码分析1—上层samples分析
  • 在Unity中实现一个简单的消息管理器
  • 自定义函数
  • 【云吞铺子】性能抖动剖析(二)
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • ​Linux Ubuntu环境下使用docker构建spark运行环境(超级详细)
  • #ifdef 的技巧用法
  • #微信小程序(布局、渲染层基础知识)
  • #我与Java虚拟机的故事#连载11: JVM学习之路
  • (02)vite环境变量配置
  • (C++)八皇后问题
  • (附源码)计算机毕业设计SSM基于java的云顶博客系统
  • (附源码)计算机毕业设计SSM教师教学质量评价系统
  • (利用IDEA+Maven)定制属于自己的jar包
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • (转)jdk与jre的区别
  • (转)Linux下编译安装log4cxx
  • (转)利用ant在Mac 下自动化打包签名Android程序