全面了解Java中的CAS机制

 更新时间:2017年10月09日 09:53:44   作者:HankingHu  
下面小编就为大家带来一篇全面了解Java中的CAS机制。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

前言

在看到Java锁机制的时候,无意中看到了CAS这个词,然后在百度查找CAS看了很多文章始终没有看的太懂,今天又在Google上查找了一些资料,才算是真正弄清楚了CAS机制。

什么是CAS

在jdk 1.5中增加的一个最主要的支持是Atomic类,比如说AtomicInteger, AtomicLong,这些类可帮助最大限度地减少在多线程中对于一些基本操作(例如,增加或减少多个线程之间共享的值)的复杂性。而这些类的实现都依赖于CAS(compare and swap)的算法。

乐观锁和悲观锁

cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时间片之间,需要进行cpu切换,也就是会发生进程的切换。切换涉及到清空寄存器,缓存数据。然后重新加载新的thread所需数据。当一个线程被挂起时,加入到阻塞队列,在一定的时间或条件下,在通过notify(),notifyAll()唤醒回来。在某个资源不可用的时候,就将cpu让出,把当前等待线程切换为阻塞状态。等到资源(比如一个共享数据)可用了,那么就将线程唤醒,让他进入runnable状态等待cpu调度。这就是典型的悲观锁的实现。独占锁是一种悲观锁,synchronized就是一种独占锁,它假设最坏的情况,并且只有在确保其它线程不会造成干扰的情况下执行,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。

但是,由于在进程挂起和恢复执行过程中存在着很大的开销。当一个线程正在等待锁时,它不能做任何事,所以悲观锁有很大的缺点。举个例子,如果一个线程需要某个资源,但是这个资源的占用时间很短,当线程第一次抢占这个资源时,可能这个资源被占用,如果此时挂起这个线程,可能立刻就发现资源可用,然后又需要花费很长的时间重新抢占锁,时间代价就会非常的高。

乐观锁思路就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。某个线程可以不让出cpu,而是一直while循环,如果失败就重试,直到成功为止。所以,当数据争用不严重时,乐观锁效果更好。比如CAS就是一种乐观锁思想的应用。

CAS(Compare and Swap )算法

CAS中有三个核心参数:

1.主内存中存放的V值,所有线程共享。

2.线程上次从内存中读取的V值A存放在线程的帧栈中,每个线程私有。

3.需要写入内存中并改写V值的B值。也就是线程对A值操作后放入到主存V中。

上面说的比较抽象,看下面的这幅图比较容易理解。

如上图中,主存中保存V值,线程中要使用V值要先从主存中读取V值到线程的工作内存A中,然后计算后变成B值,最后再把B值写回到内存V值中。多个线程共用V值都是如此操作。CAS的核心是在将B值写入到V之前要比较A值和V值是否相同,如果不相同证明此时V值已经被其他线程改变,重新将V值赋给A,并重新计算得到B,如果相同,则将B值赋给V。

如果不使用CAS机制,看看存在什么问题,假如V=1,现在Thread1要对V进行加1,Thread2也要对V进行加1,首先Thread1读取V=1到自己工作内存A中此时A=1,假设Thread2此时也读取V=1到自己的工作内存A中,分别进行加1操作后,两个线程中B的值都为2,此时写回到V中时发现V的值为2,但是两个线程分别对V进行加处理结果却只加了1有问题。

CAS核心代码

if (A==V)
{
  V = B
  return B;
}  
 else
  return V;

上面的操作是原子操作,现在来看看如果两个线程同时要对V进行加1操作使用上面的CAS机制后能不能获得正确结果。

①Thread 1和Thread2 要对V进行加1,Thread1和Thread2同时读取v值并且对V执行加1操作。

初始值 v=1,A=0, B=0。

②假设Thread1,Thread 2先读取V值赋给A,并且对A进行加1,得到B=2。

V=1,T1_A=1,T1_B=2;T2_A=1

Thread1要将T1_B写入V中,先要执行CAS操作:

if (T1_A==V)
{
  V = T1_B
  return T1_B;
}  
 else
  return V;

因为T1_A=1=V,所以执行 V=T1_B=2,此时V=2。

③Thread2也要对V执行加操作。执行加操作之后

V=2 ,T2_A=1,T2_B=2,

当Thread2要将T2_B值写要V中之前要执行CAS操作,

if (T2_A==V)
{
  V = T2_B
  return T2_B;
}  
 else
  return V;

此时T2_A=1,V=2, T2_A!=V,这时候返回V=2,给T2_A,T2_A=V=2,然后再次对T2_A进行加1,得到T2_B,此时T2_B=3,T2_A=2,比较T2_A==V,所以将T2_B的值赋给T2_V,T2_V=T2_B=3。最后结果为3。正确。

CAS中的ABA问题

如果一开始位置V得到的旧值是A,当进行赋值操作时再次读取发现仍然是A,并不能说明变量没有被其它线程改变过。有可能是其它线程将变量改为了B,后来又改回了A。大部分情况下ABA问题不会影响程序并发的正确性,如果要解决ABA问题,用传统的互斥同步可能比原子类更高效。

ABA问题的解决办法

1.在变量前面追加版本号:每次变量更新就把版本号加1,则A-B-A就变成1A-2B-3A。

2.atomic包下的AtomicStampedReference类:其compareAndSet方法首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用的该标志的值设置为给定的更新值。

以上这篇全面了解Java中的CAS机制就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 关于JSCH使用自定义连接池的说明

    关于JSCH使用自定义连接池的说明

    这篇文章主要介绍了关于JSCH使用自定义连接池的说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-09-09
  • Java制作验证码的完整实例代码

    Java制作验证码的完整实例代码

    这篇文章主要给大家介绍了关于Java制作验证码的完整实例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • java list集合排序按某一属性排序操作

    java list集合排序按某一属性排序操作

    这篇文章主要介绍了java list集合排序按某一属性排序操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08
  • Java中SpringCloud的五大组件详解

    Java中SpringCloud的五大组件详解

    这篇文章主要介绍了Java中SpringCloud的五大组件详解,Spring cloud是一个基于Spring Boot实现的服务治理工具包,在微服务架构中用于管理和协调服务,需要的朋友可以参考下
    2023-07-07
  • Java实现商城订单超时取消功能

    Java实现商城订单超时取消功能

    大多数的B2C商城项目都会有限时活动,当用户下单后都会有支付超时时间,当订单超时后订单的状态就会自动变成已取消 ,这个功能的实现有很多种方法,本文的实现方法适合大多数比较小的商城使用。具体实现方式可以跟随小编一起看看吧
    2019-12-12
  • java8 stream多字段排序的实现

    java8 stream多字段排序的实现

    这篇文章主要介绍了java8 stream多字段排序的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • Java并行执行任务的几种方案小结

    Java并行执行任务的几种方案小结

    这篇文章主要介绍了Java并行执行任务的几种方案小结,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • 深入理解Java并发编程之LinkedBlockingQueue队列

    深入理解Java并发编程之LinkedBlockingQueue队列

    本文主要介绍了Java并发编程之LinkedBlockingQueue队列,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • Java截取url参数的方法

    Java截取url参数的方法

    本文给大家带来了java截取url参数,url值得方法,代码简单易懂,非常不错,具有参考借鉴价值,感兴趣的朋友一起学习吧
    2016-08-08
  • Java实现解数独的小程序

    Java实现解数独的小程序

    最近在学习Java,然后上个月迷上了九宫格数独,玩了几天,觉得实在有趣,就想着能不能用编程来解决,于是就自己写了个,还真解决了。下面这篇文章就给大家主要介绍了Java实现解数独的小程序,需要的朋友可以参考借鉴。
    2017-01-01

最新评论