02-JUC-CAS

CAS

JUC中多数类是通过volatile和CAS来实现的,CAS本质上提供的是一种无锁方案,而Synchronized和Lock是互斥锁方案; java原子类本质上使用的是CAS,而CAS底层是通过Unsafe类实现的。

线程安全的方法有:

  • 互斥同步:ReentrantLock 和 syschronized
  • 非阻塞同步:CAS
  • ThreadLocal

第一种方法是通过锁的方式来实现线程安全,CAS是通过无锁的方式实现的,下面重点说说CAS是如何实现线程安全的

什么是CAS

  • JUC中多数类是通过volatile和CAS来实现的,CAS本质上提供的是一种无锁方案,而Synchronized和Lock是互斥锁方案; java原子类本质上使用的是CAS,而CAS底层是通过Unsafe类实现的
  • CAS操作是原子性的,所以多线程并发使用CAS更新数据时,可以不使用锁。JDK中大量使用了CAS来更新数据而防止加锁(synchronized 重量级锁)来保持原子更新

就是有一个原值A, 如果要更新为值B,在更新前先比较原值A有没有发生变化,如果没有发生变化,就将A更新为值B,如果发生了变化就不交换

用AtomicInteger 举个小栗子:多线程情况下新增一个值

AtomicInteger a  =  new AtomicInteger(0);
public int add(){
	return a.addAndGet(1);
}

CAS 有什么问题

synchronized是通过锁来保证线程安全的,是一种悲观锁策略

而CAS是一种乐观锁策略,但是在并发问题上性能更佳,它可能会出现的问题

  • ABA问题:CAS需要在操作值的时候,检查值有没有发生变化,比如没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时则会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。比如数据表来说,可以加个version字段。不过在jdk1.5后,加了个AtomicStampedReference这个类,这个类就是利用方法compareAndSet来检查当前引用是否等于预期引用,还有标志stamp是否等于预期的标志stamp,如果都相等就更新
  • CAS自旋时间太长,会导致CPU的大的执行开销

Unsafe类

我们上面提过,很多原子类是通过Unsafe实现的,

可以看一眼Unsafe类

public final class 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;
    }

    public final long getAndAddLong(Object var1, long var2, long var4) {
        long var6;
        do {
            var6 = this.getLongVolatile(var1, var2);
        } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));

        return var6;
    }

    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;
    }

    public final long getAndSetLong(Object var1, long var2, long var4) {
        long var6;
        do {
            var6 = this.getLongVolatile(var1, var2);
        } while(!this.compareAndSwapLong(var1, var2, var6, var4));

        return var6;
    }

    public final Object getAndSetObject(Object var1, long var2, Object var4) {
        Object var5;
        do {
            var5 = this.getObjectVolatile(var1, var2);
        } while(!this.compareAndSwapObject(var1, var2, var5, var4));

        return var5;
    }
    ...//省略
}

可以发现,它是通过while自旋compareAndSwap进行CAS更新(如果失败就一直自旋)。里面的compareAndSwap* 都是底层的native方法

下面是Unsafe可以实现的功能(取自网络图)

02-JUC-CAS

比如,AtomicInteger就是通过Unsafe实现的原子性

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // 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); }
    }
    // volatile  值
    private volatile int value;

    /**
     * Creates a new AtomicInteger with the given initial value.
     *
     * @param initialValue the initial value
     */
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }
    ...//省略
   public final int getAndSet(int newValue) {
        return unsafe.getAndSetInt(this, valueOffset, newValue);
    }

 
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    public final boolean weakCompareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }
    ...//省略
}

可以看到,value用了volatile来修饰,保证了线程的可见性,同时使用CAS来保证更新时的原子性

上一篇:EffectiveJava 1创建和销毁对象 1考虑用静态工厂方法代替构造器


下一篇:this 关键字的几种用法