Week 1 - Java 多线程 - CAS

前言

学习状况记录java

  • 时间:week 1
  • SMART子目标 :Java 多线程

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

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

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

非阻塞同步

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

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

CAS

乐观锁须要操做和冲突检测这两个步骤具有原子性,这里就不能再使用互斥同步来保证了,只能靠硬件来完成硬件支持的原子性操做最典型的是:比较并交换(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内的valueexpect相等,就证实没有其余线程改变过这个变量,那么就更新它为update,若是这一步的CAS没有成功,那就采用自旋的方式继续进行CAS操做,取出乍一看这也是两个步骤了啊,其实在JNI里是借助于一个CPU指令完成的。因此仍是原子操做。

CAS 的问题

  • ABA问题

    • 描述:若是一个变量初次读取的时候是 A 值,它的值被改为了 B,后来又被改回为 A,那 CAS 操做就会误认为它历来没有被改变过。
    • 解决方案:J.U.C 包提供了一个带有标记的原子引用类 AtomicStampedReference 来解决这个问题,它能够经过控制变量值的版原本保证 CAS 的正确性。大部分状况下 ABA 问题不会影响程序并发的正确性,若是真的须要解决 ABA 问题,改用传统的互斥同步可能会比原子类更高效
  • 循环时间长开销大

    • 自旋CAS(也就是不成功就一直循环执行直到成功)若是长时间不成功,会给CPU带来比较大的执行开销。
  • 只能保证一个共享变量的原子操做

    • CAS 只对单个共享变量有效,当操做涉及跨多个共享变量时CAS 无效。可是从 JDK 1.5开始,提供了AtomicReference类来保证引用对象之间的原子性,你能够把多个变量放在一个对象里来进行 CAS 操做.因此咱们可使用锁或者利用AtomicReference类把多个共享变量合并成一个共享变量来操做。

CAS与synchronized的使用情景

  • 简单的来讲CAS适用于写比较少的状况下(多读场景,冲突通常较少)
  • synchronized适用于写比较多的状况下(多写场景,冲突通常较多)
  • 对于资源竞争较少(线程冲突较轻)的状况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操做额外浪费消耗cpu资源;而CAS基于硬件实现,不须要进入内核,不须要切换线程,操做自旋概率较少,所以能够得到更高的性能。
  • 对于资源竞争严重(线程冲突严重)的状况,CAS自旋的几率会比较大,从而浪费更多的CPU资源,效率低于synchronized。

CAS 的应用

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

  1. 非阻塞算法在并发容器中的实现
  2. 非阻塞同步算法实战(一)
  3. 非阻塞同步算法实战(二)-BoundlessCyclicBarrier
  4. 非阻塞同步算法实战(三)-LatestResultsProvider

参考

  1. 《深刻理解Java虚拟机》
  2. https://www.ibm.com/developer...
相关文章
相关标签/搜索