主要参考html
★https://www.jianshu.com/p/fb6e91b013ccjava
http://zl198751.iteye.com/blog/1848575算法
https://www.xilidou.com/2018/02/01/java-cas/缓存
其余参考安全
https://my.oschina.net/huangcongmin12/blog/692907多线程
http://www.cnblogs.com/dayhand/p/3713303.html并发
https://my.oschina.net/vshcxl/blog/795495app
http://www.javashuo.com/article/p-ckzisqmq-mv.htmljvm
https://mritd.me/2017/02/06/java-cas/函数
CAS: 全称Compare and swap,字面意思:“比较并交换”,一个 CAS 涉及到如下操做:
假设内存中的原数据V,旧的预期值A,须要修改的新值B
当多个线程同时对某个资源进行CAS操做,只能有一个线程操做成功,可是并不会阻塞其余线程,其余线程只会收到操做失败的信号。可见 CAS 实际上是一个乐观锁。
为什么说当多个线程同时对某个资源进行CAS操做,只能有一个线程操做成功?多个线程访问,只有一次能成功的进行CAS操做(由于只有一个线程luck thread能够成功的捕捉到A和V相等的那一刻,而后其余线程访问时A和V确定已经不相等了)。
CAS 算法大体原理是:在对变量进行计算以前(如 ++ 操做),首先读取原变量值,称为 旧的预期值 A,而后在更新以前再获取当前内存中的值,称为 当前内存值 V,若是 A==V 则说明变量从未被其余线程修改过,此时将会写入新值 B,若是 A!=V 则说明变量已经被其余线程修改过,当前线程应当什么也不作。
CAS是乐观锁,synchronized(重量级锁)是悲观锁。
Java技术发展史先设计出的synchronized,synchronized会产生阻塞问题,后来又发展出了CAS。
阅读JDK的源码可知,J.U.C包其实是创建在CAS操做基础上的。ReentrantLock这些类的底层其实就采用的CAS操做。
能够用CAS在无锁的状况下实现原子操做,但要明确应用场合,很是简单的操做且又不想引入锁能够考虑使用CAS操做,当想要非阻塞地完成某一操做也能够考虑CAS。不推荐在复杂操做中引入CAS,会使程序可读性变差,且难以测试,同时会出现ABA问题。
不想引入锁或者想非阻塞的完成并发操做能够考虑使用CAS操做。可是CAS操做的代码可读性差难测试,复杂操做最好不用引入CAS。
public class AtomicInteger extends Number implements java.io.Serializable { // 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; public final int get() { return value; } }
1.Unsafe,是CAS的核心类,因为Java方法没法直接访问底层系统,须要经过本地(native)方法来访问,Unsafe至关于一个后门,基于该类能够直接操做特定内存的数据。
2.变量valueOffset,表示该变量值在内存中的偏移地址,由于Unsafe就是根据内存偏移地址获取数据的。
3.变量value用volatile修饰,保证了多线程之间的内存可见性。
public final int getAndAdd(int delta) { return unsafe.getAndAddInt(this, valueOffset, delta); }
上述代码已经使用到了Unsafe的方法。
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; }
假设线程A和线程B同时执行getAndAdd操做(分别跑在不一样CPU上):
整个过程当中,利用CAS保证了对于value的修改的并发安全,继续深刻看看Unsafe类中的compareAndSwapInt方法实现。
public final native boolean compareAndSwapInt(Object paramObject, long paramLong, int paramInt1, int paramInt2);
Unsafe类中的compareAndSwapInt,是一个本地方法,该方法的实现位于unsafe.cpp中。
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) UnsafeWrapper("Unsafe_CompareAndSwapInt"); oop p = JNIHandles::resolve(obj); jint* addr = (jint *) index_oop_from_field_offset_long(p, offset); return (jint)(Atomic::cmpxchg(x, addr, e)) == e; UNSAFE_END
先想办法拿到变量value在内存中的地址。
经过Atomic::cmpxchg实现比较替换,其中参数x是即将更新的值,参数e是原内存的值。
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) { int mp = os::is_MP(); __asm__ volatile (LOCK_IF_MP(%4) "cmpxchgl %1,(%3)" : "=a" (exchange_value) : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp) : "cc", "memory"); return exchange_value; }
__asm__表示汇编的开始
volatile表示禁止编译器优化
LOCK_IF_MP是个内联函数
#define LOCK_IF_MP(mp) "cmp $0, " #mp "; je 1f; lock; 1: "
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) { int mp = os::isMP(); //判断是不是多处理器 _asm { mov edx, dest mov ecx, exchange_value mov eax, compare_value LOCK_IF_MP(mp) cmpxchg dword ptr [edx], ecx } } // 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:
LOCK_IF_MP根据当前系统是否为多核处理器决定是否为cmpxchg指令添加lock前缀。
1.若是是多处理器,为cmpxchg指令添加lock前缀。
2.反之,就省略lock前缀。(单处理器会不须要lock前缀提供的内存屏障效果)
intel手册对lock前缀的说明以下:
1.确保后续指令执行的原子性。
在Pentium及以前的处理器中,带有lock前缀的指令在执行期间会锁住总线,使得其它处理器暂时没法经过总线访问内存,很显然,这个开销很大。在新的处理器中,Intel使用缓存锁定来保证指令执行的原子性,缓存锁定将大大下降lock前缀指令的执行开销。
2.禁止该指令与前面和后面的读写指令重排序。
3.把写缓冲区的全部数据刷新到内存中。
上面的第2点和第3点所具备的内存屏障效果,保证了CAS同时具备volatile读和volatile写的内存语义。
java 的 cas 利用的的是 unsafe 这个类提供的 cas 操做。
unsafe 的cas 依赖了的是 jvm 针对不一样的操做系统实现的 Atomic::cmpxchg
Atomic::cmpxchg 的实现使用了汇编的 cas 操做,并使用 cpu 硬件提供的 lock信号保证其原子性