Java之juc旅途-atomic(五)

本文详细解析了Atomic类如何利用自旋+CAS实现线程安全,以AtomicInteger为例,展示了getAndIncrement的底层实现。重点讨论了CAS的优势与限制,以及AtomicStampedReference解决ABA问题的方法。

Atomic原子类就是利用自旋+CAS来保证线程安全的。有以下几个基础类:

  • AtomicBoolean
  • AtomicInteger
  • AtomicLong
  • AtomicReference
  • AtomicStampedReference
  • AtomicMarkableReference

它还维护了一些保证原子性修改元素的集合,不过一般用不上:

  • AtomicIntegerArray
  • AtomicLongArray

甚至还可以保证对象的基础类型的原子性修改:

  • AtomicIntegerFieldUpdater
  • AtomicLongFieldUpdater

用AtomicInteger来举例子,本质就是用volatile来维护一个值。

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

    private volatile int value;
    
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

}

但是volatile只能保证这个变量的可见性,不能保证他的原子性,所以在操作的时候,用到了UnSafe类。
可以看看getAndIncrement这个类似i++的函数,可以发现,是调用了UnSafe中的getAndAddInt。

 public final int getAndIncrement() {
        return U.getAndAddInt(this, VALUE, 1);
 }



再深入看,就发现是一个 自旋+CAS了,在这个过程中,通过compareAndSwapInt比较更新value值,如果更新失败,重新获取旧值,然后更新,这样就保证了原子性的增加了。

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

 public final boolean weakCompareAndSetInt(Object o, long offset,
                                              int expected,
                                              int x) {
        return compareAndSetInt(o, offset, expected, x);
    }

CAS相对于其他锁,不会进行内核态操作,有着一些性能的提升。但同时引入自旋,当锁竞争较大的时候,自旋次数会增多。cpu资源会消耗很高。
换句话说,CAS+自旋适合使用在低并发有同步数据的应用场景。

但是,有一点需要注意的是,大部分的原子类都是不能解决ABA的问题的,所以新加了一个AtomicStampedReference类,它会维护一个Pair,然后Pair里会携带类似版本的字段stemp,在CAS额外判断stemp的值来解决ABA问题:

public boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp) {
        Pair<V> current = pair;
        return
            expectedReference == current.reference &&
            expectedStamp == current.stamp &&
            ((newReference == current.reference &&
              newStamp == current.stamp) ||
             casPair(current, Pair.of(newReference, newStamp)));
    }
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我叫小八

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值