Java并发——CAS
什么是CAS?
CAS是Compare And Swap的简称。在Java中有很多实现,比如compareAndSwapObject()方法,或者compareAndSwapInt()方法等。多用在包java.util.concurrent.atomic下的类中来实现原子性的操作。这里主要是结合compareAndSwapInt()方法介绍一下CAS的核心思想以及一些问题。
什么是原子性?
对于原子,相信学过化学的都知道,化学反应到最后不可分割的是原子。如果把我们的系统各个线程比作各种各样的化学方程式的话,其中有一部分我们就希望无论系统怎么运行,这部分都是像原子一样不能再被分割。前面说过synchronized和CAS可以实现原子性,而volatile不可以,从使用的方法上我们也可以看出,synchronized修饰的部分都是代码块或方法,也就是范围大,可以理解里面的部分都是原子的组成部分,同理CAS实际上也是一个方法,而volatile只是修饰一个变量。要实现原子性也很简单,只需要保证每次只有一个线程来运行这里面的代码即可,像我们刚接触Java时只写了主线程,那么主线程中的代码就是原子性的。上次介绍了synchronized关键字原理,现在就看看CAS在底层到底是怎么实现的。
++运算符和getAndIncreament()方法的线程安全性比较
在平常相信每个人都接触过for循环
for(int i=0;i<10;i++);
上面这段代码大家肯定都敲过很多遍。对于i++运算,其实表面看起来是i进行自增加,但其实在底层i++操作是分三步:
1、从主存中取出i的值
2、i进行加一操作
3、把值写回主存
这三步操作还是i是被volatile修饰的情况下才会进行,而平常更多的是直接操作线程副本里的i。当i是共享变量时在多个线程的情况下就会出现错误。比如线程A修改了i的值却没有写到内存,其他线程拿i的旧值进行操作,或者是线程A和线程B同时进行上面三步,那么原本是要加两次的,实际上就只加了一次。所以这是不安全的。而如果我们使用AtomicInteger类型中getAndIncreament()方法就是线程安全的,下面来分析一下这个方法为什么线程安全
public class AtomicInteger extends Number implements java.io.Serializable {public final int getAndIncrement() {return unsafe.getAndAddInt(this, valueOffset, 1);} }
我们看见这个方法中调用了unsafe.getAndAddInt方法并且传了三个参数,这三个参数分别是
this:当前的AtomicInteger类的对象。
valueOffset:可以理解为value在内存中的地址。也就是要从内存中获取最新的value值。
1:这个就很好理解了,就是每次需要增加的数,也就是自增1。
然后进入unsafe.getAndAddInt方法中
public final class Unsafe { public final int getAndAddInt(Object var1, long var2, int var4) {int var5;do {var5 = this.getIntVolatile(var1, var2);} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));return var5;} }
说一下里面的大概实现的功能。首先定义了var5,var5是通过getIntVolatile方法获取当前内存中AtomicInteger当前对象的最新值,然后比较当前对象的值,也就是var1中的value属性值和我重新在主内存中拿的值var5对应的value值是否一样,一样才会进行加一操作,并且退出while循环,最后返回var5。如果不一样就会一直从底层中去取,直到取的是和当前对象的值一样了才会执行后面的操作。现在看到这其实已经发现很多CAS存在的问题了。
1、ABA问题
上面说的比较当前值和内存中的值一样怎么即可进行下次操作。那么如果有其他线程在改过这个变量的值之后,又因为某些操作而把变量的值改回来,这时候比较的值一样了,CAS就会进行相应的操作。通俗一点说就是现在线程1需要拿共享变量的A值进行加一操作,而线程2把共享变量的值改成B值,之后又把这个变量改回A值,那么在同一时刻实际上有两个线程操作了这个值,所以破坏了原子性。这个问题解决思路就是加上版本号,对这个变量改过一次版本号就更新一次。通过版本号来记录变量的改动次数。在JDK1.5后Atomic包中就提供了一个类AtomicStampedReference来解决ABA问题。这里说一下compareAndSet方法具体的步骤是首先检查引用是否等于预期引用,也就是在内存中拿的值是否等于预期操作的值,然后检查当前的标志是否等于预期的标志,这里可以理解为版本号。
2、循环时间开销大
如果从内存中拿到的值一直和预期的值不相等,那么就会一直循环一直拿。这样会给CPU带来很大的消耗,这里稍微提一下,JVM支持处理器提供的pause指令时,这个问题才会得到一定的改善。
3、原子性不互相兼容
这个其实是说AtomicInteger类下的单独一个方法是原子性的,但是两个方法之间放在一起操作这个对象就不是原子性的,或者对多个共享变量操作时,CAS也无法保证操作是原子性。这个可以通过锁来实现(也就是需要使用一个对象下的多个方法一起进行操作的时候使用)或者通过AtomicReference类来保证引用对象之间的原子性(内部是通过把多个变量放在一个对象里来进行CAS操作)
总结
其实说到这,CAS只是其中的一个方法,而Atomic包下的类才是我们使用的主角,只不过底层在保证原子性的时候使用的是CAS方法+volatile变量,volatile变量是类中用来修饰value的,所以value会在每次操作的时候都会和主内存进行交互。CAS负责操作这个value值。CAS具体的实现是在用C++代码编写的,这里就没继续去看对应的源代码。若需要学习,完全可以去看看底层如何通过C++来实现的。
转载于:https://www.cnblogs.com/Cubemen/p/10776461.html
Java并发——CAS相关推荐
- 为什么java有cas_关于java:并发CAS机制你真的理解了嘛深入到操作系统分析
学习Java并发编程,CAS机制都是一个不得不把握的知识点.这篇文章次要是从呈现的起因再到原理进行一个解析.心愿对你有所帮忙. 一.为什么须要CAS机制? 为什么须要CAS机制呢?咱们先从一个谬误景象 ...
- 『图解Java并发』面试必问的CAS原理你会了吗?
在并发编程中我们都知道i++操作是非线程安全的,这是因为 i++操作不是原子操作. 如何保证原子性呢?常用的方法就是加锁.在Java语言中可以使用 Synchronized和CAS实现加锁效果. Sy ...
- Java并发编程,无锁CAS与Unsafe类及其并发包Atomic
为什么80%的码农都做不了架构师?>>> 我们曾经详谈过有锁并发的典型代表synchronized关键字,通过该关键字可以控制并发执行过程中有且只有一个线程可以访问共享资源,其 ...
- JAVA并发编程学习笔记之CAS操作
http://blog.csdn.net/aesop_wubo/article/details/7537960 CAS操作 CAS是单词compare and set的缩写,意思是指在set之前先比较 ...
- Java并发编程—无锁互斥机制及CAS原理
目录 一.CAS简介 二.AtomicInteger代码演示 三.CAS 实现 四.弊端 一.CAS简介 在计算机科学中,比较和交换(Conmpare And Swap)是用于实现多线程同步的原子指令 ...
- JAVA并发编程: CAS和AQS
说起JAVA并发编程,就不得不聊聊CAS(Compare And Swap)和AQS了(AbstractQueuedSynchronizer). CAS(Compare And Swap) 什么是CA ...
- 并发执行变成串行_大白话Java并发面试问题之Java 8如何优化CAS性能?
专注于Java领域优质技术,欢迎关注 来自:石杉的架构笔记 一.前言 这篇文章给大家聊一下java并发包下的CAS相关的原子操作,以及Java 8如何改进和优化CAS操作的性能. 因为Atomic系列 ...
- Java 并发实践 — ConcurrentHashMap 与 CAS
转载自 Java 并发实践 - ConcurrentHashMap 与 CAS 最近在做接口限流时涉及到了一个有意思问题,牵扯出了关于concurrentHashMap的一些用法,以及CAS的一些概念 ...
- Java 并发编程CAS、volatile、synchronized原理详解
CAS(CompareAndSwap) 什么是CAS? 在Java中调用的是Unsafe的如下方法来CAS修改对象int属性的值(借助C来调用CPU底层指令实现的): /*** * @param o ...
最新文章
- Linux Crontab 定时任务 命令详解
- pytorch 获取模型参数_剑指TensorFlow,PyTorch Hub官方模型库一行代码复现主流模型...
- javascript中parentNode,childNodes,children的应用详解
- The Intelligent Investor Feedback
- Linux驱动中获取系统时间
- 机器阅读理解中文章和问题的深度学习表示方法
- string转换成enum
- [转载] JAVA面试题大全(part_1)
- python 输入输出转web_云计算开发学习笔记:Python3 输入和输出方式
- Java中怎样创建数据库_在java中怎样创建MySQL数据库列表给个例子 爱问知识人
- 20-400-040-高可用-Flink集群的高可用搭建
- hdu 1561(树形DP+背包)
- STM32F103C8T6+LD3320语音识别模块智能灯控
- python中使用python-dotenv管理环境变量
- kubernets(k8s) 京东最新容器应用报告
- 如何取消excel密码_Excel表格技巧—Excel表格如何取消密码
- Linux用户和用户组的管理
- 虚幻4引擎学习日记----个人2d游戏开发_1
- 【开发工具】【make】make 3.82源码编译安装
- 使用DoraOS瘦客户机系统,改造华为版的升腾C92,打造超值的瘦客户机系统
热门文章
- 如何发表cscd核心论文_新手如何发表论文
- mysql 1594_【MySQL】复制1594错误(从库relaylog损坏)
- TCP UDP 协议深度解析 (未完待续)
- 计算机组装实训室管理制度,计算机组装与维护实训室管理制度.doc
- java web oracle 分页_Oracle分页的两种方式
- python修改类属性值_python编程入门到实践:直接修改属性的值和通过方法修改属性的值...
- 混合app用百分比还是rem_一次搞懂前端所有CSS长度单位,px、em、rem、rpx、%....
- 【 C 】用动态数组实现堆栈
- 398. Random Pick Index - LeetCode
- 在项目实践中用更优雅的方式处理数组问题