当前位置: 首页 > 编程笔记 >

全面了解Java中的CAS机制

司徒修能
2023-03-14
本文向大家介绍全面了解Java中的CAS机制,包括了全面了解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机制就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持小牛知识库。

 类似资料:
  • 本文向大家介绍全面了解JavaScirpt 的垃圾(garbage collection)回收机制,包括了全面了解JavaScirpt 的垃圾(garbage collection)回收机制的使用技巧和注意事项,需要的朋友参考一下 一、垃圾回收机制—GC Javascript具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存。

  • 本文向大家介绍全面了解js中的script标签,包括了全面了解js中的script标签的使用技巧和注意事项,需要的朋友参考一下 在页面中用script标签引入javascript文件(<script type="text/javascript" src="js文件地址"></script>),浏览器在渲染页面的时候,当读取到script元素时,浏览器不会以HTML或XHTML的方式处理其内容,浏览

  • 本文向大家介绍slf4j与log4j全面了解,包括了slf4j与log4j全面了解的使用技巧和注意事项,需要的朋友参考一下 推荐使用SLF4J(Simple Logging Facade for Java)作为日志的api,SLF4J是一个用于日志系统的简单Facade,允许最终用户在部署其应用时使用其所希望的日志系统。 1、Slf4j优势 与使用apache commons-logging或直接

  • 本文向大家介绍全面了解mysql中utf8和utf8mb4的区别,包括了全面了解mysql中utf8和utf8mb4的区别的使用技巧和注意事项,需要的朋友参考一下 一、简介 MySQL在5.5.3之后增加了这个utf8mb4的编码,mb4就是most bytes 4的意思,专门用来兼容四字节的unicode。好在utf8mb4是utf8的超集,除了将编码改为utf8mb4外不需要做其他转换。当然,

  • 本文向大家介绍全面了解servlet中cookie的使用方法,包括了全面了解servlet中cookie的使用方法的使用技巧和注意事项,需要的朋友参考一下 ---恢复内容开始--- Cookie是存储在客户端计算机上的文本文件,并保留了它们的各种信息跟踪的目的。 Java Servlet透明支持HTTP Cookie。 涉及标识返回用户有三个步骤: • 服务器脚本发送到浏览器的一组cookie。对

  • 本文向大家介绍Java 安全模型,你了解了吗,包括了Java 安全模型,你了解了吗的使用技巧和注意事项,需要的朋友参考一下 前言 作为一种诞生于互联网兴起时代的语言,Java 从一开始就带有安全上的考虑,如何保证通过互联网下载到本地的 Java 程序是安全的,如何对 Java 程序访问本地资源权限进行有限授权,这些安全角度的考虑一开始就影响到 Java 语言的设计与实现。可以说 Java 在这些方