CAS 原子操作: 不可分割的操作
CAS 原理
CAS(Compare And Swap),指令级别保证这是一个原子操作
三个运算符: 一个内存地址V,一个期望的值A,一个新值B
基本思路:如果地址V上的值和期望的值A相等,就给地址V赋给新值B,如果不是,不做任何操作。
循环(死循环,自旋)里不断的进行CAS操作
CAS存在的问题
ABA问题:
同时有2个线程获取当前变量值,均为A,当第一个线程对该变量进行2次操作A->B->A.此时对于另外一个线程表现出的现象为此值并未改变,并对该值进行操作。这样就出现了ABA问题。
解决的方法:
给该值添加一个版本号,每一次的操作都会修改版本号,只有修改时版本号与预期版本号相同时,才会操作成功。
CPU消耗
因为CAS操作如果不成功,将会循环进行CAS操作,则对CPU消耗过大
JAVA中的Atomic类
Atomic类的主要公共方法:
boolean compareAndSet(expect, update) 修改值,参数为预期值和更新的值.但此方法只会执行一次,成功返回true,失败返回false,自旋更新会使用该方法实现
xxx getAndSet(xxx) 赋值,如果失败,会自旋更新
AtomicInteger
public class AtomicIntegerDemo {
static AtomicInteger count = new AtomicInteger(10);
public static void main(String[] args) {
System.out.println(count.getAndIncrement());//(自旋)先获得该值,后+1
System.out.println(count.incrementAndGet());//(自旋)先+1,在获得该值
System.out.println(count.get());
}
}
运行结果:
10
12
12
AtomicReference
public class AtomicReferenceDemo {
static AtomicReference<User> reference = new AtomicReference<>();
public static void main(String[] args) {
User user = new User("lb",18);
reference.set(user);//赋值
User user1 = new User("ll",20);
reference.compareAndSet(user,user1);
System.out.println(reference.get());
User user2 = new User("bb",22);
reference.getAndSet(user2);
System.out.println(reference.get());
}
static class User{
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
}
AtomicStampedReference
带版本号的Atomic引用类
public class AtomicStampedReferenceDemo {
//設置初始值,初始版本号
static AtomicStampedReference<String> reference = new AtomicStampedReference<>("lb", 0);
public static void main(String[] args) throws InterruptedException {
String init_name = reference.getReference();
int init_stamp = reference.getStamp();
Thread thread1 = new Thread(){
@Override
public void run() {
System.out.println("修改前的name="+init_name+",版本:"+init_stamp
+".修改的结果:"+reference.compareAndSet(init_name,init_name+"_java",init_stamp,init_stamp+1));
}
};
Thread thread2 = new Thread(){
@Override
public void run() {
String name = reference.getReference();
System.out.println("修改前的name="+name+",版本:"+init_stamp
+".修改的结果:"+reference.compareAndSet(init_name,name+"_C",init_stamp,init_stamp+1));
}
};
thread1.start();
thread1.join();
thread2.start();
thread2.join();
System.out.println("最终: name="+reference.getReference()+",版本:"+reference.getStamp());
}
}
运行结果:
修改前的name=lb,版本:0.修改的结果:true
修改前的name=lb_java,版本:0.修改的结果:false
最终: name=lb_java,版本:1
注意:
AtomicMarkableReference,boolean 有没有动过
AtomicStampedReference 动过几次
AtomicIntegerArray
public class AtomicIntegerArrayDemo {
static int[] arr = new int[]{2,1};
static AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(arr);
public static void main(String[] args) {
System.out.println(atomicIntegerArray.get(0));//参数为下标
atomicIntegerArray.set(0,3);
System.out.println(atomicIntegerArray.get(0));
System.out.println(arr[0]);
}
}
运行结果:修改的值是AtomicIntegerArray中存储的副本,而不是数组本身
2
3
2