CAS
作者:互联网
CAS
cas是什么
Compare-And-Swap 比较并交换,是一条CPU的并发原语
判断内存中某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的
如果主内存中的值和期望中的值一样,就修改为现在想要的值,如果主内存中的值和期望中的值不一样,就不修改
package com.audition.cas;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created with IntelliJ IDEA.
*
* @Auther: 两杯水
* @Date: 2021/03/01/10:11
* @Description:
* cas是什么?比较并交换
*
*/
public class CasDemo1 {
public static void main(String[] args) {
//得到的默认值是5
AtomicInteger atomicInteger = new AtomicInteger(5);
//compareAndSet:比较内存中的值是否是5,如果是就修改为2021
System.out.println(atomicInteger.compareAndSet(5, 2021)+ "\t current data:" +atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(5, 1234)+ "\t current data:" +atomicInteger.get());
}
}
cas的底层原理
比较当前工作内存中的值和主内存中的值,如果相同则执行规定操作,否则继续比较,直到主内存中的值和工作内存中的值一致为止
自旋锁和UnSafe类
UnSafe类:CAS核心类,Java无法直接访问底层系统,其内部方法可以向C指针一样操作内存,java CAS操作依靠于UnSafe类,UnSafe根据内存偏移地址获取数据的,
所以说cas是一种系统原语,原语的执行必须是连续的,在执行过程中不能被终端,CAS是cpu的原子指令,所以不会造成数据不一致问题
CAS应用:
CAS有3个操作数,内存值V,旧的预期值A,要修改的更新值B
当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做
CAS缺点:
循环时间长:如果CAS失败,会一直进行尝试,会给CPU造成很大开销
只能保证一个共享变量的原子操作
引来ABA问题
ABA问题:
两个线程同时取主物理内存中的同一内存中的数据,但是由于其中一个线程比另一个线程快,中间修改了内存数据多次,在另一个线程访问之前,又把内存数据修改为第一次获取值,。当另一个线程回去访问时,并不知道数据已经修改了多次了,只看到数据一样
解决ABA问题
代码示例:
package com.audition.cas;
import lombok.*;
import java.util.concurrent.atomic.AtomicReference;
/**
* Created with IntelliJ IDEA.
*
* @Auther: 两杯水
* @Date: 2021/03/01/11:24
* @Description:原子引用 AtomicReference
*/
@Getter
@Setter
@AllArgsConstructor
@ToString
class User{
String userName;
int age;
}
public class CasDemo2 {
public static void main(String[] args) {
User z3 = new User("z3",23);
User li4 = new User("li4",25);
AtomicReference<User> reference = new AtomicReference<>();
reference.set(z3);
System.out.println(reference.compareAndSet(z3, li4)+"\t" +reference.get().toString());
System.out.println(reference.compareAndSet(z3, li4)+"\t" +reference.get().toString());
}
}
新增一种机制,修改版本号(类似时间戳)
package com.audition.cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* Created with IntelliJ IDEA.
*
* @Auther: 两杯水
* @Date: 2021/03/01/11:34
* @Description: AtomicStampedReference
*/
public class ABADemo {
static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);
public static void main(String[] args) {
System.out.println("=========以下是ABA问题的产生==============");
new Thread(()->{
atomicReference.compareAndSet(100,101);
atomicReference.compareAndSet(101,100);
},"t1").start();
new Thread(()->{
//暂停一秒钟,保证t1线程完成了一次ABA操作
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicReference.compareAndSet(100,200)+"\t"+atomicReference.get());
},"t2").start();
//暂停一会线程
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("=========以下是ABA问题的解决==============");
new Thread(()->{
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"\t第一次版本号"+stamp);
//暂停1秒钟T3线程
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+"\t第二次版本号"+stamp);
atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+"\t第三次版本号"+stamp);
},"t3").start();
new Thread(()->{
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"\t第一次版本号"+stamp);
//暂停3秒钟T4线程,确保t3完成了一次ABA操作
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean andSet = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1);
System.out.println(Thread.currentThread().getName()+"\t修改成功否:"+andSet + "\t当前实际版本号"+atomicStampedReference.getStamp());
System.out.println(Thread.currentThread().getName()+"\t当前实际最新值: "+ atomicStampedReference.getReference());
},"t4").start();
}
}
标签:compareAndSet,CAS,System,内存,println,atomicStampedReference,out 来源: https://blog.csdn.net/weixin_44413260/article/details/114281715