CAS(CompareAndSet)是比较之后并交换;
/**
* 1.CAS 是什么?==》compareAndSet
* 比较并交换:期望值和主内存中的值要是一样的话,就把值更新并快照到主内存中
*/
public class CASDemo {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(5);
System.out.println(atomicInteger.compareAndSet(5,2020)+"\t current data:"+atomicInteger.get());
}
}
小结:真实值和期望值相同,就会修改成功,真实值和期望值不同,修改就不会成功。
AtomicInteger.getAndcrement()方法的源代码:
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
从上面的源码,引出来一个问题,UnSafe类(rt.jar/sun/misc)是什么?
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
1)UnSafe:是CAS的核心类,由于Java方法无法直接访问底层系统,需要通过本地(native)方法来访问,UnSafe相当于一个后门,基于该类可以直接操作特定内存的数据。UnSafe类存在sun,misc包中,其内部方法操作可以想C的指针一样直接操作内存,因为Java中CAS操作的执行依赖于UnSafe类的方法。
注意:UnSafe类中的所有方法都是native修饰的,也就是说UnSafe类中的方法都直接调用操作系统底层资源执行相应任务。
CAS是什么?
CAS的全称是Compare-And-Swap它是一条CPU并发原语。
它的功能是判断内存某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的。
CAS并发原语提现在Java语言中就是sun.misc.UnSafe类中的各个方法,调用UnSafe类中的CAS方法,JVM会帮我们实现出CAS汇编指令。这是一种完全依赖于硬件的功能,通过它实现了原子操作。再次强调,由于CAS是一种系统原语,原语属于操作系统用于范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成所谓的数据不一致问题。
思路:
1.(UnSafe.getAnddInt)
2.底层汇编:
3.总结:
CAS(CompareAndSwap):比较当前工作内存中的值和内存中的值,如果相同则执行规定操作,否则继续比较知道内存和工作内存中的值一致为止。
CAS应用:
CAS有三个操作数,内存值V,就是预期值A,要修改的更新值B。当且仅当预存值A和内存值V相同时,将内存值V改为B,否则什么都不做。
4.CAS缺点:
1)循环时间长,开销大。(里边有个dowhile如果CAS失败,会一直进行尝试如果CAS长时间一直不成功,可能会给CPU带来很大的开销)
2)只能保证一个共享变量的原子操作。
3)引出来ABA为题。
1)ABA问题是怎么产生的?
CAS会导致ABA问题:CAS算法实现了一个虫咬前提需要取出某时刻的数据并在当下时刻比较并替换,那么在这个时间差内,会导致数据的变化。
比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且线程tow进行了一些操作将变成了B,然后线程two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后线程one操作成功。尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。
2)原子引用:
import java.util.concurrent.atomic.AtomicReference;
class User{
String userName;
int age;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User(String userName, int age) {
this.userName = userName;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", age=" + age +
'}';
}
}
public class AtomicReferenceDemo {
public static void main(String[] args) {
User z3 = new User("z3",11);
User l4 = new User("l4",25);
AtomicReference<User> atomicReference = new AtomicReference<>();
atomicReference.set(z3);
System.out.println(atomicReference.compareAndSet(z3, l4)+"\t "+atomicReference.get());
}
}
如何解决ABA问题?理解原子引用+新增一种机制,那就是修改版本号(类似时间戳)。(AtomicStampedRederence带时间戳的原子)