CAS

基本概念

悲观锁:
它总是认为每次访问共享资源时会发生冲突,所以必须对每次数据操作加上锁,以保证临界区的程序同一时间只能有一个线程在执行

乐观锁:
乐观锁总是假设对共享资源的访问没有冲突,线程可以不停地执行,无需加锁也无需等待。而一旦多个线程发生冲突,乐观锁通常是使用一种称为CAS的技术来保证线程执行的安全性。由于无锁操作中没有锁的存在,因此不可能出现死锁的情,也就是说乐观锁天生免疫死锁

乐观锁多用于“读多写少”的环境,避免频繁加锁影响性能;而悲观锁多用于“写多读少”的环境,避免频繁失败和重试影响性能

CAS:比较并交换(Compare And Swap)。在CAS中,有三个值:

  • V:要更新的变量(var)【临界资源】
  • E:预期值(expected) 【旧值】
  • N:新值(new)

过程如下:

Java实现CAS的原理

Java中的CAS都是native方法,交给底层的JVM使用C或者C++实现。在Java中,有个Unsafe类,在sun.misc包中,其中有几个方法,它们都是public native的:

public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

Unsafe中对CAS的实现是C++写的,它的具体实现和操作系统,CPU都有关系。
Linux的X86下主要通过cmpxchgl这个指令在CPU级完成CAS操作的,但在多处理器的情况下必须使用lock指令加锁来完成。当然不同的操作系统和处理起的实现有所不同。

Unsafe类里面还有其它方法用于不同的用途。比如支持线程挂起和恢复的park和unpark,LockSupport类底层就是调用了这个两个方法。

原子操作-AtomicInteger类源码解析

JDK提供了一些用于原子操作的类,在java.util.concurrent.atomic包下面。这些类大概的用途:

  • 原子更新基本类型
  • 原子更新数组
  • 原子更新引用
  • 原子更新字段(属性)

以AtomicInteger类的getAndAdd(int delta)方法为例:

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

    private volatile int value;
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }

从源码可以看出AtomicInteger的getAndAdd是调用的Unsafe.getAndAddInt和volatile来实现的
 

    public final int getAndAddInt(Object o, long offset, int delta) {
        int v;
        do {
            v= this.getIntVolatile(o, offset);
        } while(!this.compareAndSwapInt(o, offset, v, v+ delta));

        return v; // v = v + delta
    }

CAS是“无锁”的基础,它允许更新失败。所以经常会与while循环搭配,在失败后不断去尝试。它的目的是保证循环体内的语句至少会被执行一次这样才能保证return的值v是我们期望的值。

CAS实现原子操作的三大问题

1. ABA问题

所谓ABA问题,就是一个值原来是A,变成了B,又变成了A。这个时候使用CAS是检查不出来的,但实际上却被更新了两次。--加上版本号或者时间戳。在JDK 1.5以后,JDK的atomic包里提供了一个类AtomicStampedReference类来解决ABA问题。这个类的compareAndSet方法的作用是首先检查当前引用是否等于预期引用,并且这个检查当前标志是否等于预期标志,如果二者相等,才使用CAS设置为新的值和标志。

2. 循环时间长开销大

CAS多与自旋结合。如果自旋CAS长时间不成功,会占用大量的CPU资源。解决思路是让JVM支持处理器提供的pause指令。【pause指令让自旋失败时cpu睡眠一段时间再自旋,从而降低频率】

3. 之能保证一个共享变量的原子操作

1. 使用JDK 1.5开始提供的AtomicReference类保证对象之间的原子性,把多个变量放到一个对象里面进行CAS操作;

2. 使用锁,锁内的临界区代码可以保证只有当前线程能操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值