前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >原子操作 CAS CompareAndSwap

原子操作 CAS CompareAndSwap

作者头像
平凡的学生族
发布2019-05-25 08:52:43
1.7K0
发布2019-05-25 08:52:43
举报
文章被收录于专栏:后端技术后端技术

参考

1. AtomicInteger实现CAS

使用AtomicIntegerAtomicBoolean等原子操作类可以完成原子操作。它的各种操作都是基于Unsafe类的,你可以看到函数的画风都是下面这样:

代码语言:javascript
复制
//  AtomicIneger.java
public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

Unsafe的实现是native方法,无法查看源码:

代码语言:javascript
复制
// Unsafe.java
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

2. 底层原子操作:Unsafe类

参考Unsafe类 Unsafe涉及底层硬件级别的原子操作,其函数大多是native的,如下:

代码语言:javascript
复制
// Unsafe.java
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);

程序员不能直接调用它,只有授信的代码才能获得该类的实例,当然JDK库里面的类是可以随意使用的。 我们只需要知道,它是原子操作的底层实现即可。

3. volatile + AtomicIntegerFieldUpdater实现CAS:netty中的应用

参考张龙netty教程的P.81~P.84 先说结论:volatile + AtomicIntegerFieldUpdater的实现方案相比AtomicInteger能节省更多内存空间。

3.1 两种int的CAS实现

  1. AtomicInteger类实现了CAS,但如果频繁使用其实例,会占用不少内存:64位机器上,一个AtomicInteger对象地址占用8个字节,其内部维护的int值value占用4个字节。也就是说,在每个用到CAS操作的整形值的地方,都需要维护一个AtomicInteger对象,也就是占用4+8=12字节
  2. 但如果能用volatile + AtomicIntegerFieldUpdater的方法实现,就能减少存储。你只需要维护一个静态变量AtomicIntegerFieldUpdater,并为需要用到CAS操作的int值声明为volatile即可。只需要先分配一个静态变量,然后在每个用到CAS操作的整形值的地方,都只需要维护一个volatile int变量,也就是占用4字节

3.2 netty的CAS实现方案的选择

在netty中,需要频繁创建/销毁大量的ByteBuf对象,用于存储信息。为了及时销毁ByteBuf对象,需要使用引用计数。netty构建了AbstractReferenceCountedByteBuf,实现了引用计数的功能。

在程序运行的过程中,会创建大量的AbstractReferenceCountedByteBuf实例,每个需要在其内部维护一个整形值,代表引用计数。实例可能会被不同对象同时引用,当它不被引用时,应当及时释放空间。为了让引用计数在多线程下有序地增减,引用计数应当用CAS实现

那么我们要使用AtomicInteger实现CAS吗?

  • AbstractReferenceCountedByteBuf实例里的引用计数用一个AtomicInteger变量实现,确实简单。但这样每个实例都要为它分配4+8=12字节的空间。
  • 但如果我们用volatile int + AtomicIntegerFieldUpdater,只需先为静态变量AtomicIntegerFieldUpdater分配空间,然后每个实例只需要占用4字节即可。能节约很多内存。 正因为如此,netty使用了AtomicIntegerFieldUpdater来实现引用计数的CAS。

3.3 从netty源码学习CAS的实现

我们看下源码:

代码语言:javascript
复制
// AbstractReferenceCountedByteBuf.java
public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf {
    private static final long REFCNT_FIELD_OFFSET;
    private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> refCntUpdater =
            AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");

    private volatile int refCnt = 1;
    ...
    @Override
    public ByteBuf retain(int increment) {
        return retain0(checkPositive(increment, "increment"));
    }
    private ByteBuf retain0(final int increment) {
        int oldRef = refCntUpdater.getAndAdd(this, increment);
        ...
        return this;
    }
    ...
}

AbstractReferenceCountedByteBuf通过retain和release来增加、释放引用。而这两个函数是基于refCntUpdaterrefCnt的CAS操作。 这样,每个AbstractReferenceCountedByteBuf实例只需要为refCnt分配4字节空间即可,相比于AtomicInteger的12字节,节省了很多空间。

4. ABA问题

参考什么是CAS问题

AtomicInteger、AtomicReference等类会带来ABA问题。 ABA问题,就是要维护的变量被替换后,又设置回来。类实例将无法辨别它被替换过。 举个例子,假设有一个变量x:

  1. 线程1试图用cas把x从A设置为C,所以它先查询x的值。(在这瞬间,线程切换)
  2. 线程2用cas把x设置为B
  3. 线程2用cas把x设置为A
  4. (线程切换回来)线程1查询到x的值为A,于是cas理所当然地把x改为了C。

问题是:线程1在查询x的过程中,x的值已经经历了A->B->A的转变,而线程1对此无所知。这就是ABA问题了。

4.1 AtomicReference的ABA问题

我们看到compareAndSet的源码:

代码语言:javascript
复制
// AtomicReference.java

/**
 * Atomically sets the value to the given updated value
 * if the current value {@code ==} the expected value.
 * @param expect the expected value
 * @param update the new value
 * @return {@code true} if successful. False return indicates that
 * the actual value was not equal to the expected value.
 */
public final boolean compareAndSet(V expect, V update) {
    return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}

根据注释,它的比较是基于地址数值比较的,而与"equals"方法没有关系。stackoverflow上的问题 也印证了这一点。 也就是说,AtomicReference的ABA问题指的是"其引用可能会改变",而不是指"引用的变量的值可能会改变"

解决ABA问题: AtomicStampedReference<T>

AtomicStampedReference

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.04.28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 参考
  • 1. AtomicInteger实现CAS
  • 2. 底层原子操作:Unsafe类
  • 3. volatile + AtomicIntegerFieldUpdater实现CAS:netty中的应用
    • 3.1 两种int的CAS实现
      • 3.2 netty的CAS实现方案的选择
        • 3.3 从netty源码学习CAS的实现
        • 4. ABA问题
          • 4.1 AtomicReference的ABA问题
          • 解决ABA问题: AtomicStampedReference<T>
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档