CAS


本文介绍了CAS算法的应用场景及原理。

1、volatile不能保证原子性

假设有个场景,在单实例环境下,记录访问某接口的用户数。为此,设计了一个计数器,代码如下:

public class MyApp {

    /* volatile 不能保证变量复合操作的原子性 */
    private volatile int count = 0;

    public void updateVistors() {
        count++;
    }
}

虽然volatile修饰的共享变量count的内存可见性能被保证,但count++操作的原子性则不然,当多线程并发修改count值时,count是线程不安全的。

2、synchronized是大开销操作

解决这个问题,不少人的第一反应是将count++的操作加锁(synchronized),如下:

public class MyApp {

    private int count = 0;
    /* 带锁的方法 synchronized */
    public synchronized void updateVistors() {
        count++;
    }
}

当然,synchronized关键字修饰后,count++操作的原子性可见性都能被保证,但synchronized是相对重的锁。只是为了实现小小的自增需求,就引入该锁显得“大材小用”。即使在JDK1.6之后,synchronized已经因为引入了偏向锁、轻量级锁而被优化很多,但当多线程激烈竞争时,大量线程阻塞、唤醒,相对“无锁并发”实际是大开销的操作。

3、CAS(原子类)解决方案

假如使用线程安全的原子类,由于使用“无锁并发”技术(CAS),开销相对synchronized要小很多。

public class MyApp1 {

    private final AtomicLong al = new AtomicLong(0);

    public void updateVisitors() {
        al.incrementAndGet();
    }

}

相对于synchronized这样的pessimistic locking(悲观锁),CAS是一种更轻量的optimistic locking(乐观锁)技术。

CAS(Compare and Swap)技术的核心思想如下:

This algorithm compares the contents of a memory location to a given
value and, only if they are the same, modifies the contents of that
memory location to a given new value.

This is done as a single atomic operation. The atomicity
guarantees that the new value is calculated based on up-to-date
information; if the value had been updated by another thread in the
meantime, the write would fail.

The result of the operation must indicate whether it performed the
substitution; this can be done either with a simple Boolean response
(this variant is often called compare-and-set), or by returning
the value read from the memory location (not the value written to it).

  1. A memory location V where value has to be replaced
  2. Old value A which was read by thread last time
  3. New value B which should be written over V

CAS says “I think V should have the value A; if it does, put B there,
otherwise don’t change it but tell me I was wrong
.” CAS is an
optimistic technique—it proceeds with the update in the hope of
success, and can detect failure if another thread has updated the
variable since it was last examined.

画成图:
在这里插入图片描述

4、原子类的核心源码分析

在JDK8之前,代码如是写道:

public final long incrementAndGet() {
    for (;;) {
        long old = get();  //  the value read by thread last time
        long next = old + 1;  // new value to be written over V
        if (compareAndSet(current, next))
          return next;
    }
}

JDK8之后,采用了更能说明其本质的写法:

public final long incrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}

这样写有什么好处呢?
unsafe.getAndAddLong()会被JIT优化。在x86架构下,这句代码只对应到一句CPU指令LOCK XADD,这个指令比普通的循环CAS的性能更好。

在多线程激烈竞争的场景下,synchronized可能比原子变量性能更好(多线程空自旋会影响性能?),不过多数情况下(in realistic contention levels )原子变量比synchronized性能更好。若有疑问请参考

Java8还引入了LongAdder类,其表述如下:

This class is usually preferable to AtomicLong when multiple threads
update a common sum that is used for purposes such as collecting
statistics, not for fine-grained synchronization control. Under low
update contention, the two classes have similar characteristics. But
under high contention, expected throughput of this class is
significantly higher, at the expense of higher space consumption.

So LongAdder is not always a replacement for AtomicLong. We need to
consider the following aspects:

1)When no contention is present AtomicLong performs better.
2)LongAdder will allocate Cells (a final class declared in
abstract class Striped64) to avoid contention which consumes memory.
So in case we have a tight memory budget we should prefer AtomicLong.

这段话抓住重点:

  • 1)激烈冲突下,LongAdder吞吐更高,但是消耗内存更大
  • 2)低冲突下,使用AtomicLong
  • 3)在类似数据统计,而不是精确同步的场景下,更推荐LongAdder

5、CAS的问题

CAS的问题有三:

  • 1)ABA问题
  • 2)多线程长期自旋忙等锁,造成CPU开销大
  • 3)CAS只能对单个变量实现原子操作

在专题文章 【Java如何实现原子操作】中已有说明

6、更深入:CAS的原理

CAS通过调用JNI的代码实现的。JNI:Java Native Interface为JAVA本地调用,允许java调用其他语言。
compareAndSwapInt就是借助C来调用CPU底层指令实现的。
(具体不再深入)

7、CAS在并发包的地位

CAS + AQS是并发包的基石!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nQq4aot1-1618652367800)(http://note.youdao.com/yws/res/36415/WEBRESOURCE9804eb32fef3cca7be224d1305445907)]

8、CAS思想在业务代码中的应用

巧用CAS解决数据一致性问题

参考文献

  • https://dzone.com/articles/how-cas-compare-and-swap-java
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值