Java cas原理
JDK concurrent包下有很多原子类AtomicInteger.java等,提供了原子更新操作。
下面是一个cas demo的方法
public static void casDemo(){ AtomicInteger atomicInteger = new AtomicInteger(1); int x = atomicInteger.getAndIncrement(); System.out.println("x = " + x); }
这里getAndIncrement会自赠1,并且是线程安全的。
跟进JDK源码
AtomicInteger.getAndIncrement()方法
/** * Atomically increments the current value, * with memory effects as specified by {@link VarHandle#getAndAdd}. * * <p>Equivalent to {@code getAndAdd(1)}. * * @return the previous value */ public final int getAndIncrement() { return U.getAndAddInt(this, VALUE, 1); }
注释可以看出方法的作用是原子更新变量,继续跟进U.getAndAddInt()方法
/** * Atomically adds the given value to the current value of a field * or array element within the given object {@code o} * at the given {@code offset}. * * @param o object/array to update the field/element in * @param offset field/element offset * @param delta the value to add * @return the previous value * @since 1.8 */ @HotSpotIntrinsicCandidate public final int getAndAddInt(Object o, long offset, int delta) { int v; do { v = getIntVolatile(o, offset); } while (!weakCompareAndSetInt(o, offset, v, v + delta)); return v; }
这里我用的JDK 10可能会有点不一样,继续跟进weakCompareAndSetnt()方法
@HotSpotIntrinsicCandidate public final boolean weakCompareAndSetInt(Object o, long offset, int expected, int x) { return compareAndSetInt(o, offset, expected, x); }
继续跟进compareAndSetInt()方法
/** * Atomically updates Java variable to {@code x} if it is currently * holding {@code expected}. * * <p>This operation has memory semantics of a {@code volatile} read * and write. Corresponds to C11 atomic_compare_exchange_strong. * * @return {@code true} if successful */ @HotSpotIntrinsicCandidate public final native boolean compareAndSetInt(Object o, long offset, int expected, int x);
这里是一个native方法,native方法,即本地方法,调用其他语言实现的方法。openjdk可以看出jvm主要是c++实现,网上看到别人贴出来openjdk源码
// Adding a lock prefix to an instruction on MP machine // VC++ doesn't like the lock prefix to be on a single line // so we can't insert a label after the lock prefix. // By emitting a lock prefix, we can define a label after it. #define LOCK_IF_MP(mp) __asm cmp mp, 0 \ __asm je L0 \ __asm _emit 0xF0 \ __asm L0: inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) { // alternative for InterlockedCompareExchange int mp = os::is_MP(); __asm { mov edx, dest mov ecx, exchange_value mov eax, compare_value LOCK_IF_MP(mp) cmpxchg dword ptr [edx], ecx } }
这段源码可以看到用了汇编的cmpxchg指令,这个在多cpu情况下会在指令前加lock前缀,保证指令执行的原子性。单核cpu不会加lock前缀。
这条指令如何保证原子性,就是cpu的事情了,网上有资料说是锁住总线、锁住共享内存等
cas优点:
1. 不用阻塞线程,不会进行线程上下文切换
缺点:
1. 线程自旋, cpu空转
2. 不能保证多个变量的原子性
3. 不能感知到ABA问题,可以考虑使用版本号解决
参考
1. http://zl198751.iteye.com/blog/1848575
2. https://blog.csdn.net/xiuye2015/article/details/53406432
Please call me JiangYouDang!