Java CAS机制详解
CAS目的:
在多线程中为了保持数据的准确性,避免多个线程同时操作某个变量,很多情况下利用关键字synchronized实现同步锁,使用synchronized关键字修可以使操作的线程排队等待运行,可以说是一种悲观策略,认为线程会修改数据,所以开始就把持有锁的线程锁住,其他线程只能是挂起状态,等待锁的释放,所以同步锁带来了问题:
主要的效率问题:在线程执行的时候,获得锁的线程在运行,其他被挂起的线程只能等待着持有锁的线程释放锁才有机会运行(现在JVM可能根据持有锁的时间来操作线程是否是被挂起还是自旋等待),在效率上都浪费在等待上。很可能这种锁是没有必要的,其他线程没有修改数据。在很多的线程切换的时候,由于有同步锁,就要涉及到锁的释放,加锁,这又是一个很大的时间开销。这里涉及到操作系统上的知识,关于线程之间的切换(被挂起和恢复)中间还要经历中断,时间片等等。
上面说了这么多,现在我们追求的是一种效率高,还要保证数据的安全的一种方法。
与锁(阻塞机制)的方式相比有一种更有效地方法,非阻塞机制,同步锁带来了线程执行时候之间的阻塞,而这种非阻塞机制在多个线程竞争同一个数据的时候不会发生阻塞的情况,这样在时间上就可以节省出很多的时间。
想到这里,知道volatile的可能会想到用volatile,使用volatile不会造成阻塞,volatile保证了线程之间的内存可见性和程序执行的有序性可以说已经很好的解决了上面的问题,但是一个很重要的问题就是,volatile不能保证原子性,对于复合操作,例如i++这样的程序包含三个原子操作:取指,增加,赋值。在《Java并发编程实战》这本书上有这样的一句话:变量的新值依赖于旧值时就不能使用volatile变量。实际上就是说的类似于i++这样的操作。具体详见:volatile关键字解析
什么是CAS:
现在采取的是CAS(Compare And Swap比较和交换)解决了volatile不能保证原子性。CAS通常比锁定要快得多,但这取决于争用的程度。因为如果读取和比较之间的值发生变化,CAS可能会强制重试,所以不能说某个方法就是绝对的好。CAS的主要问题是编程比锁定更困难。还好jdk提供了一些类用于完成基本的操作。
CAS主要包含三个操作数,内存位置V,进行比较的原值A,和新值B。当位置V的值与A相等时,CAS才会通过原子方式用新值B来更新V,否则不会进行任何操作。无论位置V的值是否等于A,都将返回V原有的值。通俗点说:我认为V地址的值应该是A,如果是,V地址的值更新为B,否则不修改并告诉V的值实际为多少(无论如何这个值都会通知到V)。上面说到了同步锁 是一种悲观策略,CAS是一种乐观策略,每次都开放自己,不用担心其他线程会修改变量等数据,如果其他线程修改了数据,那么CAS会检测到并利用算法重新计算。CAS也是同时允许一个线程修改变量,其他的线程试图修改都将失败,但是相比于同步锁,CAS对于失败的线程不会将他们挂起,他们下次仍可以参加竞争,这也就是非阻塞机制的特点。
下面用代码简单的实现CAS原理:
/**
* Created with IDEA
*
* @author DuzhenTong
* @Date 2018/2/1
* @Time 11:52
*/
public class SimpleCAS { private int value; public SimpleCAS(int value) {
this.value = value;
} public synchronized int get(){
return value;
} public synchronized int compareAndSwap(int expectedValue, int newValue){
int oldValue = value;//获取旧值
if(oldValue == expectedValue){//如果期望值与当前V位置的值相同就给予新值
value = newValue;
}
return oldValue;//返回V位置原有的值
} public synchronized boolean compareAndSet(int expectedValue, int newValue){
return (expectedValue == compareAndSwap(expectedValue, newValue));
} public static void main(String[] args) {
SimpleCAS simpleCAS = new SimpleCAS(3);
simpleCAS.compareAndSet(5, 10);
System.out.println(simpleCAS.get());//3 SimpleCAS simpleCAS1 = new SimpleCAS(1);
simpleCAS1.compareAndSet(1, 6);
System.out.println(simpleCAS1.get());//6
} }
从运行结果可以看出代码的原理:设置一个初始值(内存位置),期望值和新值进行比较,如果期望值和初始值一致,返回新值,否则返回初始值。意思是你在修改在一个变量A,假如它原来的值是3,所以你预期它是3,如果在你修改的时候,它被别的线程更新为5,那么就不符合你的预期,你的修改也不会生效
从Java5开始引入了底层的支持,在这之前需要开发人员编写相关的代码才可以实现CAS。在原子变量类Atomic***中(例如AtomicInteger、AtomicLong)可以看到CAS操作的代码,在这里的代码都是调用了底层(核心代码调用native修饰的方法)的实现方法。在AtomicInteger源码中可以看getAndSet方法和compareAndSet方法之间的关系,compareAndSet方法调用了底层的实现,该方法可以实现与一个volatile变量的读取和写入相同的效果。在前面说到了volatile不支持例如i++这样的复合操作,在Atomic***中提供了实现该操作的方法。JVM对CAS的支持通过这些原子类(Atomic***)暴露出来,供我们使用。
CAS带来的问题:
ABA问题:CAS在操作的时候会检查变量的值是否被更改过,如果没有则更新值,但是带来一个问题,最开始的值是A,接着变成B,最后又变成了A。经过检查这个值确实没有修改过,因为最后的值还是A,但是实际上这个值确实已经被修改过了。为了解决这个问题,在每次进行操作的时候加上一个版本号,每次操作的就是两个值,一个版本号和某个值,A——>B——>A问题就变成了1A——>2B——>3A。在jdk中提供了AtomicStampedReference类解决ABA问题,用Pair这个内部类实现,包含两个属性,分别代表版本号和引用,在compareAndSet中先对当前引用进行检查,再对版本号标志进行检查,只有全部相等才更新值。
时间问题:看起来CAS比锁的效率高,从阻塞机制变成了非阻塞机制,减少了线程之间等待的时间。每个方法不能绝对的比另一个好,在线程之间竞争程度大的时候,如果使用CAS,每次都有很多的线程在竞争,而锁可以避免这些状况,相反的情况,如果线程之间竞争程度小,使用CAS是一个很好的选择。
Java CAS机制详解的更多相关文章
- Java 反射机制详解(下)
续:Java 反射机制详解(上) 三.怎么使用反射 想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属 ...
- Java 反射机制详解(上)
一.什么是反射 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java ...
- java异常处理机制详解
java异常处理机制详解 程序很难做到完美,不免有各种各样的异常.比如程序本身有bug,比如程序打印时打印机没有纸了,比如内存不足.为了解决这些异常,我们需要知道异常发生的原因.对于一些常见的异常,我 ...
- Java SPI机制详解
Java SPI机制详解 1.什么是SPI? SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制.SPI是一种动态替换发现的机制, 比如有个 ...
- Java反射机制详解
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反 ...
- Java GC机制详解
垃圾收集 Garbage Collection 通常被称为“GC”,本文详细讲述Java垃圾回收机制. 导读: 1.什么是GC 2.GC常用算法 3.垃圾收集器 4.finalize()方法详解 5. ...
- CAS机制详解
目录 1. 定义 2. 实现原理 3. 无版本号CAS实战说明 4. CAS机制在Java中的应用 5. CAS的缺点 1. CPU开销过大 2. 不能保证代码块的原子性 3. ABA问题 6. JA ...
- Java CAS 原理详解
1. 背景 在JDK 5之前Java语言是靠 synchronized 关键字保证同步的,这会导致有锁.锁机制存在以下问题: 在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问 ...
- java反射机制详解 及 Method.invoke解释
JAVA反射机制 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为ja ...
随机推荐
- Scrum方法论
产品负责人: 代表客户或未来游戏玩家.产品负责人需要确保所有有趣的功能都能在游戏中实现,还负责对游戏完整观感的理解. Scrum主管: 代表理性思维.需要主持每日Scrum会议,并确保每个人都在执行任 ...
- 【java学习笔记】Properties
Properties:可以持久化的映射,规定键和值的类型是String. Properties对象必须放到.properties文件中,其中properties文件默认为西欧编码,也因此不存储中文. ...
- python 循环语句 函数 模块
python循环语句 while循环语法结构 当需要语句不断的重复执行时,可以使用while循环 while expression: while_suite 语句ehile_suite会被连续不断的循 ...
- PCI9054芯片的型号说明及购买建议
个脚,这也是大部分人用到的:而"BI"结尾的是BGA封装的,225个脚,较少用到,对它不再多说. 这几种系列量产的时间如下: 年11月 年8月 年2月 年 年 年到2006年期间, ...
- java Socket实现简单在线聊天(一)
最近的项目有一个在线网页交流的需求,由于很久以前做过的demo已经忘记的差不多了,因此便重新学习一下. 我计划的大致实现步骤分这样几大步: 1.使用awt组件和socket实现简单的单客户端向服务端持 ...
- Java之split方法
Java之split方法 1.间隔号"." (1)str.split(".") String str = "10.156.35.87"; S ...
- ATA接口寄存器描述
ATA接口寄存器描述 .ATA接口的三种数据传输方式 位. )MDMA(Multiword DMA)传输,用于数据传输.ATA主机控制器向ATA设备下达MDMA传输命令后,等待设备向主机发送DMARQ ...
- ASP.NET性能调试
该文转自mx5721的博客:http://blog.csdn.net/mx5721/article/details/9138135 设计考虑 性能和安全的考虑 应用程序逻辑划分的考虑:逻辑分层,然后使 ...
- Codeforces Round #427 (Div. 2) D - Palindromic characteristics
本题是个简单的区间dp 最近都没时间做题了,被我妈强制喊回去,然后颓废了10天(回家也没发控制住自己= = 我的锅),计划都打乱了,本来还报名了百度之星,然后没时间参加 #include<cma ...
- spring+jidi读取property的配置文件
在Spring项目中,你可能需要从properties文件中读入配置注入到bean中,例如数据库连接信息,memcached server的地址端口信息等,这些配置信息最好独立于jar包或者war包, ...