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