【问题标题】:Is CAS a loop like spin?CAS 是像自旋一样的循环吗?
【发布时间】:2017-12-01 00:08:31
【问题描述】:

我在阅读sun.misc.Unsafe.Java的代码时遇到了一个问题。

CAS 是像自旋一样的循环吗?

起初,我认为 CAS 只是一种低寿命的原子操作。但是,当我尝试查找函数compareAndSwapInt 的源代码时,我发现cpp 代码是这样的:

jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest, jbyte compare_value) {
    assert(sizeof(jbyte) == 1, "assumption.");
    uintptr_t dest_addr = (uintptr_t)dest;
    uintptr_t offset = dest_addr % sizeof(jint);
    volatile jint* dest_int = (volatile jint*)(dest_addr - offset);
    jint cur = *dest_int;
    jbyte* cur_as_bytes = (jbyte*)(&cur);
    jint new_val = cur;
    jbyte* new_val_as_bytes = (jbyte*)(&new_val);
    new_val_as_bytes[offset] = exchange_value;
    while (cur_as_bytes[offset] == compare_value) {
        jint res = cmpxchg(new_val, dest_int, cur);
        if (res == cur) break;
            cur = res;
        new_val = cur;
        new_val_as_bytes[offset] = exchange_value;
    }
    return cur_as_bytes[offset];
}

我在这个原子函数中看到了“when”和“break”。

这是一种旋转方式吗?

相关代码链接:

http://hg.openjdk.java.net/jdk8u/jdk8u20/hotspot/file/190899198332/src/share/vm/prims/unsafe.cpp

http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/07011844584f/src/share/classes/sun/misc/Unsafe.java

http://hg.openjdk.java.net/jdk8u/jdk8u20/hotspot/file/55fb97c4c58d/src/share/vm/runtime/atomic.cpp

【问题讨论】:

  • 没有原子比较和交换字节,因此必须使用循环和比较和交换 int 值来模拟。

标签: java c++ jvm


【解决方案1】:

CAS 是一个 single 操作,返回值 10 表示此操作是否成功,因为您正在执行 compareAndSwapInt 您想要此操作成功,因此操作会重复,直到它工作。

我认为您也将此与spin lock 混淆了,这基本上意味着在此值为“1”时执行某些操作(例如);所有其他线程一直等到这个值为零(通过compareAndSwap),这实际上意味着某个线程完成了工作并释放了锁(这被称为release/acquire语义)

【讨论】:

    【解决方案2】:

    CAS 操作不是自旋,它是硬件级别的原子操作。在 x86 和 SPARC 处理器上,CAS 是一条指令,它支持 intlong 操作数。

    实际上,Atomic::cmpxchg int / long 重载是在 x86 上使用单个 cmpxchgl/cmpxchgq 指令生成的。

    您正在查看的是 Atomic::cmpxchg 单-byte 重载,它解决了 CAS 指令在 byte 级别模拟 CAS 的限制.它通过对位于与byte 相同地址的int 执行CAS,然后仅检查其中一个byte 并在CAS 由于其他3 个字节的更改而失败时重复此操作。比较和交换仍然是原子的,只是有时需要重试,因为它覆盖了不必要的字节。

    【讨论】:

      【解决方案3】:

      CAS 通常是一个硬件指令,例如整数加法或比较(只是速度较慢)。指令本身可能被分解为所谓的微码的几个步骤,并且可能确实包含低级循环或阻塞等待另一个处理器组件。然而,这些是处理器架构的实现细节。还记得 CS 中的任何问题都可以通过添加另一层间接来解决的说法吗?这也适用于这里。 Java中的原子操作实际上可能涉及以下几层:

      1. Java 方法签名。
      2. 实现它的 C(++) JNI 方法。
      3. C(++)“编译器内在”,例如 GCC 的 __atomic_compare_exchange
      4. 实际的处理器指令。
      5. 实现此指令的微码。
      6. 所述微码要使用的附加层,例如缓存一致性协议等。

      我的建议是不要担心所有这些是如何工作的,除非任何一种情况都适用:

      • 出于某种原因,它不起作用。这可能是由于平台错误造成的。
      • 太慢了。

      单元测试可以帮助您识别前一种情况。基准测试可以帮助您识别后一种情况。但需要指出的是,如果 Java 提供给您的 CAS 速度较慢,您可能无法自己编写更快的 CAS。因此,在这种情况下,最好的办法是更改数据结构或数据流,以进一步减少所需的线程同步量。

      【讨论】:

        猜你喜欢
        • 2018-01-03
        • 2010-12-03
        • 1970-01-01
        • 2017-03-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-12
        • 1970-01-01
        相关资源
        最近更新 更多