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机制详解的更多相关文章

  1. Java 反射机制详解(下)

    续:Java 反射机制详解(上) 三.怎么使用反射 想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属 ...

  2. Java 反射机制详解(上)

    一.什么是反射 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java ...

  3. java异常处理机制详解

    java异常处理机制详解 程序很难做到完美,不免有各种各样的异常.比如程序本身有bug,比如程序打印时打印机没有纸了,比如内存不足.为了解决这些异常,我们需要知道异常发生的原因.对于一些常见的异常,我 ...

  4. Java SPI机制详解

    Java SPI机制详解 1.什么是SPI? SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制.SPI是一种动态替换发现的机制, 比如有个 ...

  5. Java反射机制详解

    Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反 ...

  6. Java GC机制详解

    垃圾收集 Garbage Collection 通常被称为“GC”,本文详细讲述Java垃圾回收机制. 导读: 1.什么是GC 2.GC常用算法 3.垃圾收集器 4.finalize()方法详解 5. ...

  7. CAS机制详解

    目录 1. 定义 2. 实现原理 3. 无版本号CAS实战说明 4. CAS机制在Java中的应用 5. CAS的缺点 1. CPU开销过大 2. 不能保证代码块的原子性 3. ABA问题 6. JA ...

  8. Java CAS 原理详解

    1. 背景 在JDK 5之前Java语言是靠 synchronized 关键字保证同步的,这会导致有锁.锁机制存在以下问题: 在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问 ...

  9. java反射机制详解 及 Method.invoke解释

    JAVA反射机制 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为ja ...

随机推荐

  1. JVM笔记5-对象的访问定位。

    java虚拟机中指定一个栈内存的引用指向了堆内存中的对象.这样说只是笼统的说法.而指向堆内存中的对象就一定是栈引用所需要的那个对象吗?其实并不定. 这就需要知道对象的访问定位方式有两种: 1.使用句柄 ...

  2. HTTP状态码作用

    文章目录 状态码分类 常见的状态码 1xx消息 2xx成功 3xx重定向 4xx客户端错误 5xx服务器错误 HTTP状态码的作用是:web服务器用来告诉客户端,发生了什么事. 状态码位于HTTP R ...

  3. 关于VS2013调试IIS应用源代码时无法进入断点的问题总结

    调试无法进入断点 前言:今天再次遇到之前调试无法进入断点的问题,本来想不写呢觉得没什么只是又犯了同样的错误,但是我发现这个问题我分析起来还是挺费劲的,我仔细想了想原因, 是因为自己对之前的错误没有进行 ...

  4. R语言︱函数使用技巧(循环、if族/for、switch、repeat、ifelse、stopifnot)

    每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- 后续加更内容: 应用一:if族有哪些成员呢?- ...

  5. Excel 2010高级应用-柱形图(一)

    今天,做项目低保真,是在excel中画图,这也是我第一次在excel中画图. 每次做过的东西或者学到的新东西,我必须要把他们记录下来,这样到时再次用到它们时可以很容易地找到. 下面介绍做柱形图的过程: ...

  6. Linux显示cat帮助信息并退出

    Linux显示cat帮助信息并退出 youhaidong@youhaidong-ThinkPad-Edge-E545:~$ cat --help 用法:cat [选项]... [文件]... 将[文件 ...

  7. CentOS中配置NFS服务

    1.服务器端安装rpcbind.nfs-utils.nfs-server包 yum install nfs-utils -y 2.修改服务器端配置文件,添加需要共享的文件夹. vim /etc/exp ...

  8. 决策树系列(四)——C4.5

    预备知识:决策树.ID3 如上一篇文章所述,ID3方法主要有几个缺点:一是采用信息增益进行数据分裂,准确性不如信息增益率:二是不能对连续数据进行处理,只能通过连续数据离散化进行处理:三是没有采用剪枝的 ...

  9. 对ajax回调函数的研究

    1.1开发中遇到的问题 最近开发中我和同事都碰到这样的问题,我们使用jQuery的ajax方法做服务端的校验,在success方法里将验证结果存储到一个js的公共变量或者是页面里的隐藏域,接下来的代码 ...

  10. PHP二维数组搜索返回数组

    $ar = array( 2 => array( 'catid' => 2, 'catdir' => 'notice', ), 5 => array( 'catid' => ...