Java 多线程 - CAS

2019-10-06  本文已影响0人  Richard_易

前言

记录在学习线程安全知识点中,关于CAS的有关知识点。

线程安全是指:多个线程不管以何种方式访问某个类,并且在主调代码中不需要进行同步,都能表现正确的行为。

常见的线程安全实现方法分为不可变对象、线程互斥同步、非阻塞同步、线程本地存储等方案,本文要讲的就是非阻塞同步中的核心CAS.

非阻塞同步

从处理问题的方式上说,互斥同步属于一种悲观的并发策略。

随着硬件指令集的发展,我们可以采用基于冲突检查的乐观并发策略,通俗地说,就是先行操作,如果没有其他线程争用共享数据,那操作就成功了;如果共享数据有争用,产生了冲突,那就再采取其他的补偿措施(最常见的补偿措施就是不断地重试,直到成功为止),这种乐观的并发策略的许多实现偶读不需要把线程挂起,因此这种同步操作称为非阻塞同步。

CAS

乐观锁需要操作和冲突检测这两个步骤具备原子性,这里就不能再使用互斥同步来保证了,只能靠硬件来完成硬件支持的原子性操作最典型的是:比较并交换(Compare-and-Swap,CAS)CAS 指令需要有 3 个操作数,分别是内存地址 V、旧的预期值 A 和新值 B。当执行操作时,只有当 V 的值等于 A,才将 V 的值更新为 B。

各种Atomic开头的原子类,内部都应用到了CAS。就拿AtomicInteger为例。

J.U.C 包里面的原子类 AtomicInteger 的方法调用了 Unsafe 类的 CAS 操作。

image

看看AtomicInteger对象一次自增,CAS起了什么作用,以下代码是 incrementAndGet() 的源码,可以看到内部调用了 Unsafe 对象的 getAndAddInt()

image

以下代码是 getAndAddInt()源码,var1 指示对象内存地址,var2指示该字段相对对象内存地址的偏移,var4 指示操作需要加的数值,这里为 1。通过 getIntVolatile(var1, var2) 得到旧的预期值,通过调用 compareAndSwapInt() 来进行 CAS比较如果该字段内存地址中的值等于var5,那么就更新内存地址为 var1+var2 的变量为 var5+var4

image

compareAndSwapInt(var1, var2, var5, var5 + var4 其实换成compareAndSwapInt(obj, offset, expect, update)比较清楚,意思就是如果obj内的valueexpect相等,就证明没有其他线程改变过这个变量,那么就更新它为update,如果这一步的CAS没有成功,那就采用自旋的方式继续进行CAS操作,取出乍一看这也是两个步骤了啊,其实在JNI里是借助于一个CPU指令完成的。所以还是原子操作。

CAS 的问题

CAS与synchronized的使用情景

CAS 的应用

使用 CAS 原子指令来处理对数据的并发访问,这是非阻塞算法得以实现的基础。关于非阻塞算法是属于J.U.C中并发容器部分的知识,属于比较难的内容。目前先引用几篇文章。作为记录,之后有机会再详细学习。

  1. 非阻塞算法在并发容器中的实现

  2. 非阻塞同步算法实战(一)

  3. 非阻塞同步算法实战(二)-BoundlessCyclicBarrier

  4. 非阻塞同步算法实战(三)-LatestResultsProvider

参考

  1. 《深入理解Java虚拟机》
  2. https://www.ibm.com/developerworks/cn/java/j-lo-concurrent/
上一篇 下一篇

猜你喜欢

热点阅读