CAS
compare and swap 比较并交换。包行三个操作数:内存位置、预期原值、更新值。
执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,如果不匹配,处理器不做任何操作,多个线程同时执行CAS操作只有一个会成功。
硬件级别保证
CAS是JDK提供的非阻塞原子性操作,它通过硬件保证了比较-更新的原子性。
CAS是一条CPU的原子指令(cmpxchg指令),不会造成所谓的数据不一致问题,Unsafe提供的CAS方法底层实现即为CPU指令cmpxchg。
执行cmpchg指令的时候,会判断当前系统是否为多核系统,如果是就给总线加锁,只有一个线程会对总线加锁成功,加锁成功之后会指向cas操作,也就是说CAS的原子性实际上是CPU实现的。多线程情况下性能会比较好。
UnSafe
是CAS的核心类,由于Java方法无法直接访问底层系统,需要通过本地native方法来访问,unsafe相当于一个后门,基于该类可以直接操作特定内存的数据。
unSafe类存在与sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因为Java的CAS操作的执行依赖于unSafe类的方法。注意unSafe类中的所有方法都是native修饰的,也就是说Unsafe类中的方法都直接调用操作系统底层资源执行相应任务。
变量valueOffset,表示该变量值在内存中的偏移地址,因为unsafe就是根据内存便宜地址获取数据的。
变量value用volatile修饰,保证了多线程之间的内存可见性。
i++线程不安全 使用atomicInteger.getAndIncrement()
AtomicInteger类主要利用CAS+volatile+native方法来保证原子操作,从而避免synchronized的高开销,执行效率大为提示。
CAS是一种系统原语,原语属于操作系统用语范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成所谓的数据不一致问题。
自旋锁 spinlock
指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁, 当线程发现锁被占用时,会不断循环判断锁的状态,直到获取。这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU 。
CAS缺点
引出ABA问题
CAS算法实现一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并替换,那么在这个时间差类会导致数据的变化。
比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且线程two进行了一些操作将值变成了B, 然后线程two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后线程one操作成功。
解决:版本号时间戳原子引用