> 文章列表 > Java并发基石_CAS原理实战02_CAS实现原理

Java并发基石_CAS原理实战02_CAS实现原理

Java并发基石_CAS原理实战02_CAS实现原理

文章目录

    • 什么是CAS?
    • CAS的实现原理是什么?
    • cmpxchg指令怎么保证多核心下的线程安全?
    • 什么是ABA问题?
    • 如何解决ABA问题呢?

什么是CAS?

CAS,全称CompareAndSwap,比较并替换。
CAS包含了三个操作数,内存位置值V,期望值A,新值B,如果内存位置值V与期望值A匹配,处理器就将内存位置值更新为新值B,否则不做任何操作。无论发生哪种情况,它都会在CAS指令之前返回该位置的值。

sun.misc.Unsafe类中提供了对CAS操作的支持

/*** Atomically update Java variable to <tt>x</tt> if it is currently* holding <tt>expected</tt>.* @return <tt>true</tt> if successful*/public final native boolean compareAndSwapObject(Object o, long offset,Object expected,Object x);/*** Atomically update Java variable to <tt>x</tt> if it is currently* holding <tt>expected</tt>.* @return <tt>true</tt> if successful*/public final native boolean compareAndSwapInt(Object o, long offset,int expected,int x);/*** Atomically update Java variable to <tt>x</tt> if it is currently* holding <tt>expected</tt>.* @return <tt>true</tt> if successful*/public final native boolean compareAndSwapLong(Object o, long offset,long expected,long x);
  • o:表示要操作的对象
  • offset:表示要操作对象中属性地址的偏移量
  • expected:表示期望值
  • x:表示新值

CAS的实现原理是什么?

CAS通过调用Java native interface的代码实现,允许Java调用其他语言,compareandswap系列的方法就是借助c语言调用cpu底层指令(cmpxchg)来实现的,cpu执行该指令时就实现了比较并替换的操作。

cmpxchg指令怎么保证多核心下的线程安全?

系统底层进行CAS操作的时候,首先会判断当前系统是否为多核,如果是就会对总线进行加锁,且只有一个线程可以可以对总线加锁成功,加锁成功之后就会执行CAS操作。

什么是ABA问题?

CAS操作需要在操作值的时候判断值有没有变化,如果没有变化则更新。但是如果一个值原来是A,在CAS方法执行之前,另一个线程先把A的值修改为B,然后又修改为A,那么CAS操作就会误认为A的值没有发生过变化,但是实际上A是发生过变化的。

代码模拟测试:

package juc.cas;import java.util.concurrent.atomic.AtomicInteger;public class ABADemo {
//    原子变量,是使用CAS实现的public static AtomicInteger a = new AtomicInteger(1);public static void main(String[] args) {Thread main = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("操作线程"+Thread.currentThread().getName()+",初始值"+a.get());try {int expectNum = a.get();int newNum = expectNum + 1;Thread.sleep(1000);boolean isCASSuccess = a.compareAndSet(expectNum,newNum);System.out.println("操作线程"+Thread.currentThread().getName()+",CAS操作"+isCASSuccess);}catch (Exception e){e.printStackTrace();}}},"主线程");Thread other = new Thread(new Runnable() {@Overridepublic void run() {try {
//                    确保main线程先执行Thread.sleep(20);a.incrementAndGet();System.out.println("操作线程"+Thread.currentThread().getName()+",increment:"+a.get());a.decrementAndGet();System.out.println("操作线程"+Thread.currentThread().getName()+",decrement:"+a.get());}catch (Exception e){e.printStackTrace();}}},"干扰线程");main.start();other.start();}
}

Java并发基石_CAS原理实战02_CAS实现原理
可以看到,CAS操作是成功了的。

如何解决ABA问题呢?

引入版本号机制,A每被修改一次版本号的值就会+1,当执行CAS操作的时候,拿到期望值的版本号与当前值的版本号进行比对,如果一致,则执行CAS操作。
Java并发基石_CAS原理实战02_CAS实现原理
图中的stamp就是版本号。

	 * @param expectedReference the expected value of the reference//期望引用* @param newReference the new value for the reference//新值引用* @param expectedStamp the expected value of the stamp//期望引用的版本号* @param newStamp the new value for the stamp//新值引用的版本号* @return {@code true} if successful
public boolean compareAndSet(V   expectedReference,V   newReference,int expectedStamp,int newStamp) {Pair<V> current = pair;returnexpectedReference == current.reference &&expectedStamp == current.stamp &&((newReference == current.reference &&newStamp == current.stamp) ||casPair(current, Pair.of(newReference, newStamp)));}

代码模拟测试:

package juc.cas;import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;public class ABADemo {
//    原子变量,是使用CAS实现的public static AtomicStampedReference<Integer> a = new AtomicStampedReference(new Integer(1),1);public static void main(String[] args) {Thread main = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("操作线程"+Thread.currentThread().getName()+",初始值"+a.getReference());try {Integer expectReference = a.getReference();Integer newReference = expectReference + 1;Integer expectStamp = a.getStamp();Integer newStamp = expectStamp+1;Thread.sleep(1000);boolean isCASSuccess = a.compareAndSet(expectReference,newReference,expectStamp,newStamp);System.out.println("操作线程"+Thread.currentThread().getName()+",CAS操作"+isCASSuccess);}catch (Exception e){e.printStackTrace();}}},"主线程");Thread other = new Thread(new Runnable() {@Overridepublic void run() {try {
//                    确保main线程先执行Thread.sleep(20);a.compareAndSet(a.getReference(),a.getReference()+1,a.getStamp(),a.getStamp()+1);System.out.println("操作线程"+Thread.currentThread().getName()+",increment:"+a.getReference());a.compareAndSet(a.getReference(),a.getReference()-1,a.getStamp(),a.getStamp()-1);System.out.println("操作线程"+Thread.currentThread().getName()+",decrement:"+a.getReference());}catch (Exception e){e.printStackTrace();}}},"干扰线程");main.start();other.start();}
}

Java并发基石_CAS原理实战02_CAS实现原理
引入版本号之后,CAS就失败了。

在这里插入图片描述
文章参考:小刘老师讲源码