CAS
什么是CASCAS与传统synchronized区别CAS实现——基于java封装汇编的UNSAFE AtomicInteger以及相关原子类
原子类更新基本类型原子类更新数组类型基本使用示例原子类更新引用类型使用示例原子类更新字段类型基础使用示例通过上述代码我们可以总结出CAS字段必须符合以下要求:用ABA问题做一个收尾 小结与补充 CAS 什么是CAS
CAS全称Compare-And-Swap,说白就是就是比较当前的值与旧值是否相等若相等则进行修改操作,该类常用于多线程共享变量的修改操作。而其底层实现也是基于硬件平台的汇编指令,JVM只是封装其调用仅此而已。
CAS基础使用示例
如下所示,可以看出使用封装CAS操作的AtomicInteger操作多线程共享变量无需我们手动加锁,因为避免过多人为操作这就大大减少了多线程操作下的失误。
使用原子类操作共享数据
public class CasTest { private AtomicInteger count = new AtomicInteger(); public void increment() { count.incrementAndGet(); } // 使用 AtomicInteger 后,不需要加锁,也可以实现线程安全 public int getCount() { return count.get(); } public static void main(String[] args) { }}
使用sync锁操作数据
public class Test { private int i=0; public synchronized int add(){ return i++; }}
CAS与传统synchronized区别除了上述区别以外CAS工作原理是基于乐观锁且操作是原子性的,与synchronized的悲观锁相比,效率也会相对高一些。
但即便如此CAS仍然存在两个问题:
可能存在长时间CAS:如下代码所示,这就是AtomicInteger底层的UNSAFE类如何进行CAS的具体代码 ,可以看出这个CAS操作需要拿到volatile变量后在进行循环CAS才有可能成功这就很可能存在自旋循环,从而给cpu带来很大的执行开销。public final int getAndSetInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var4)); return var5; }
CAS只能对一个变量进行原子操作:为了解决这个问题,JDK 1.5之后通过AtomicReference使得变量可以封装成一个对象进行操作
ABA问题:总所周知CAS就是比对当前值与旧值是否相等,在进行修改操作,假设我现在有一个变量值为A,我改为B,再还原为A,这样操作变量值是没变的?那么CAS也会成功不就不合理吗?这就好比一个银行储户想查询概念转账记录,如果转账一次记为1,如果按照ABA问题的逻辑,那么这个银行账户转账记录次数有可能会缺少。为了解决这个问题JDK 1.5提供了AtomicStampedReference,通过比对版本号在进行CAS操作,那么上述操作就会变为1A->2B->3A,由于版本追加,那么我们就能捕捉到当前变量的变化了。
CAS实现——基于java封装汇编的UNSAFE代码也很简单,就是拿到具有现场可见性的volatile变量在进行原子操作的CAS
public final int getAndAddInt(Object paramObject, long paramLong, int paramInt) { int i; do i = getIntVolatile(paramObject, paramLong); while (!compareAndSwapInt(paramObject, paramLong, i, i + paramInt)); return i; } public final long getAndAddLong(Object paramObject, long paramLong1, long paramLong2) { long l; do l = getLongVolatile(paramObject, paramLong1); while (!compareAndSwapLong(paramObject, paramLong1, l, l + paramLong2)); return l; } public final int getAndSetInt(Object paramObject, long paramLong, int paramInt) { int i; do i = getIntVolatile(paramObject, paramLong); while (!compareAndSwapInt(paramObject, paramLong, i, paramInt)); return i; } public final long getAndSetLong(Object paramObject, long paramLong1, long paramLong2) { long l; do l = getLongVolatile(paramObject, paramLong1); while (!compareAndSwapLong(paramObject, paramLong1, l, paramLong2)); return l; } public final Object getAndSetObject(Object paramObject1, long paramLong, Object paramObject2) { Object localObject; do localObject = getObjectVolatile(paramObject1, paramLong); while (!compareAndSwapObject(paramObject1, paramLong, localObject, paramObject2)); return localObject; }
AtomicInteger以及相关原子类 原子类更新基本类型AtomicBoolean: 原子更新布尔类型。AtomicInteger: 原子更新整型。AtomicLong: 原子更新长整型。
原子类更新数组类型AtomicIntegerArray: 原子更新整型数组里的元素。AtomicLongArray: 原子更新长整型数组里的元素。AtomicReferenceArray: 原子更新引用类型数组里的元素。
基本使用示例import java.util.concurrent.atomic.AtomicIntegerArray;public class AtomicIntegerArrayDemo { public static void main(String[] args) throws InterruptedException { AtomicIntegerArray array = new AtomicIntegerArray(new int[] { 0, 0 }); System.out.println(array);// 索引1位置+2 System.out.println(array.getAndAdd(1, 2)); System.out.println(array); }}
原子类更新引用类型AtomicReference: 原子更新引用类型。AtomicStampedReference: 原子更新引用类型, 内部使用Pair来存储元素值及其版本号。AtomicMarkableReferce: 原子更新带有标记位的引用类型。
使用示例import java.util.concurrent.atomic.AtomicReference;public class AtomicReferenceTest { public static void main(String[] args){ // 创建两个Person对象,它们的id分别是101和102。 Person p1 = new Person(101); Person p2 = new Person(102); // 新建AtomicReference对象,初始化它的值为p1对象 AtomicReference ar = new AtomicReference(p1); // 通过CAS设置ar。如果ar的值为p1的话,则将其设置为p2。 ar.compareAndSet(p1, p2); Person p3 = (Person)ar.get(); System.out.println("p3 is "+p3); System.out.println("p3.equals(p1)="+p3.equals(p1)); System.out.println("p3.equals(p2)="+p3.equals(p2)); }}class Person { volatile long id; public Person(long id) { this.id = id; } public String toString() { return "id:"+id; }}
原子类更新字段类型AtomicIntegerFieldUpdater: 原子更新整型的字段的更新器。AtomicLongFieldUpdater: 原子更新长整型字段的更新器。AtomicStampedFieldUpdater: 原子更新带有版本号的引用类型。AtomicReferenceFieldUpdater: 上面已经说过此处不在赘述。
基础使用示例import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;public class TestAtomicIntegerFieldUpdater { public static void main(String[] args){ TestAtomicIntegerFieldUpdater tIA = new TestAtomicIntegerFieldUpdater(); tIA.doIt(); } public AtomicIntegerFieldUpdater
1、变量必须使用volatile保证可见性2、必须是当前对象可以访问到的类型才可进行操作‘3、只能是实例变量而不是类变量,即不可以有static修饰符4、包装类也不行
用ABA问题做一个收尾public class AtomicStampedReference
代码示例,我们下面就用other代码模拟干扰现场,如果other现场先进行cas更新再还原操作,那么main线程的版本号就会过时,CAS就会操作失败
public class AtomicStampedReferenceTest { private static AtomicStampedReference
AtomicMarkableReference,它不是维护一个版本号,而是维护一个boolean类型的标记,标记值有修改,了解一下。