学习状况记录java
记录在学习线程安全知识点中,关于CAS
的有关知识点。算法
线程安全是指:多个线程无论以何种方式访问某个类,而且在主调代码中不须要进行同步,都能表现正确的行为。安全
常见的线程安全实现方法分为不可变对象、线程互斥同步、非阻塞同步、线程本地存储等方案,本文要讲的就是非阻塞同步中的核心CAS
.多线程
从处理问题的方式上说,互斥同步属于一种悲观的并发策略。并发
随着硬件指令集的发展,咱们能够采用基于冲突检查的乐观并发策略,通俗地说,就是先行操做,若是没有其余线程争用共享数据,那操做就成功了;若是共享数据有争用,产生了冲突,那就再采起其余的补偿措施(最多见的补偿措施就是不断地重试,直到成功为止),这种乐观的并发策略的许多实现偶读不须要把线程挂起,所以这种同步操做称为非阻塞同步。less
乐观锁须要操做和冲突检测这两个步骤具有原子性,这里就不能再使用互斥同步来保证了,只能靠硬件来完成。硬件支持的原子性操做最典型的是:比较并交换(Compare-and-Swap,CAS)。CAS 指令须要有 3 个操做数,分别是内存地址 V、旧的预期值 A 和新值 B。当执行操做时,只有当 V 的值等于 A,才将 V 的值更新为 B。ide
各类Atomic开头的原子类,内部都应用到了CAS
。就拿AtomicInteger
为例。性能
J.U.C 包里面的原子类 AtomicInteger
的方法调用了 Unsafe
类的 CAS
操做。学习
看看AtomicInteger
对象一次自增,CAS
起了什么做用,如下代码是 incrementAndGet()
的源码,能够看到内部调用了 Unsafe
对象的 getAndAddInt()
。spa
如下代码是 getAndAddInt()
源码,var1
指示对象内存地址,var2
指示该字段相对对象内存地址的偏移,var4
指示操做须要加的数值,这里为 1。经过 getIntVolatile(var1, var2)
获得旧的预期值,经过调用 compareAndSwapInt()
来进行 CAS
比较,若是该字段内存地址中的值等于 var5
,那么就更新内存地址为 var1+var2
的变量为 var5+var4
。
compareAndSwapInt(var1, var2, var5, var5 + var4
其实换成compareAndSwapInt(obj, offset, expect, update)
比较清楚,意思就是若是obj
内的value
和expect
相等,就证实没有其余线程改变过这个变量,那么就更新它为update
,若是这一步的CAS
没有成功,那就采用自旋的方式继续进行CAS操做,取出乍一看这也是两个步骤了啊,其实在JNI里是借助于一个CPU指令完成的。因此仍是原子操做。
ABA问题
循环时间长开销大
CAS
(也就是不成功就一直循环执行直到成功)若是长时间不成功,会给CPU带来比较大的执行开销。只能保证一个共享变量的原子操做
CAS
只对单个共享变量有效,当操做涉及跨多个共享变量时CAS
无效。可是从 JDK 1.5开始,提供了AtomicReference
类来保证引用对象之间的原子性,你能够把多个变量放在一个对象里来进行 CAS
操做.因此咱们可使用锁或者利用AtomicReference
类把多个共享变量合并成一个共享变量来操做。使用 CAS 原子指令来处理对数据的并发访问,这是非阻塞算法得以实现的基础。关于非阻塞算法是属于J.U.C中并发容器部分的知识,属于比较难的内容。目前先引用几篇文章。做为记录。