AtomicInteger的原子性,底层用的为什么要用CAS而不是synchronized?
CAS的认识
CAS的全称为Compare-And-Swap,它是一条CPU并发原语。它的功能是判断内存某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的。
CAS并发原语体现在JAVA语言中就是sun.misc.Unsafe类中的各个方法。调用UnSafe类中的CAS方法,JVM会帮我们实现出CAS汇编指令。这是一种完全依赖于硬件的功能,通过它实现了原子操作。在执行过程中不允许被中断,不会造成数据不一致问题。
CAS:比较当前工作内存中的值和主内存中的值,如果相同则执行规定操作,否则继续比较知道主内存和工作内存中的值一致为止。
CAS应用:CAS有3个操作数,内存值V,旧的预期值A,要修改的更新值B。
AtomicInteger的底层代码
unsafe类中的所有方法都是native方法
//AtomicInteger
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
//valueOffset表示该变量值在内存中的偏移地址,因为Unsafe就是根据内存偏移地址获取数据的
//unsafe.getAndAddInt
public final int getAndAddInt(Object var1, long var2, int var4){
int var5;
do{
var5 = this.getIntVolatile(var1, var2);
}while (!this.compareAndSwapInt(var1,var2,var5,var5+var4));
return var5;
}
CAS的缺点:
①循环时间长,开销大(如果CAS失败,会一直进行尝试,如果长时间一直不成功,可能会给CPU带来很大的开销。)
②只能保证一个共享变量的原子操作
当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原 子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作
③引出了ABA问题
原子引用AtomicReference:
class User{
public User(String userName, int age) {
this.userName = userName;
this.age = age;
}
String userName;
int age;
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", age=" + age +
'}';
}
}
public class AtomicReferenceDemo {
public static void main(String[] args) {
AtomicReference<User> userAtomicReference = new AtomicReference<>();
User u1 = new User("zhangsan",22);
User u2 = new User("lisi",20);
userAtomicReference.set(u1);
System.out.println(userAtomicReference.compareAndSet(u1,u2)+"\t"+userAtomicReference.get().toString());
System.out.println(userAtomicReference.compareAndSet(u1,u2)+"\t"+userAtomicReference.get().toString());
}
}
AtomicStampedReference版本号原子引用:ABA问题的解决
/**
* ABA问题的解决
*/
public class AtomicStampedReferenceDemo {
static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);
public static void main(String[] args) {
/**
* ABA问题的产生
*/
new Thread(()->{
atomicReference.compareAndSet(100,101);
atomicReference.compareAndSet(101,100);
},"t1").start();
new Thread(()->{
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicReference.compareAndSet(100,2019);
System.out.println(atomicReference.get());
},"t2").start();
/**
* ABA问题的解决
*/
new Thread(()->{
int stamp = atomicStampedReference.getStamp();
try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
atomicStampedReference.compareAndSet(100,101,stamp,stamp+1);
stamp = atomicStampedReference.getStamp();
atomicStampedReference.compareAndSet(101,100,stamp,stamp+1);
System.out.println(atomicStampedReference.getStamp() + " =" + atomicStampedReference.getReference());
},"t3").start();
new Thread(()->{
int stamp = atomicStampedReference.getStamp();
try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
boolean result = atomicStampedReference.compareAndSet(100,2019,stamp,stamp+1);
System.out.println(result+" "+atomicStampedReference.getStamp()+ "=="+ atomicStampedReference.getReference());
},"t4").start();
}
}