java并发:CAS、Unsafe

CAS

CAS即CompareandSwap,其具体的意思是比较并交换;⽐较是否和给定的数值⼀致,如果⼀致则修改,不⼀致则不修改。

它是JDK提供的非阻塞原子性操作,它通过硬件保证了“比较、更新”操作的原子性

在 Intel 处理器中,⽐较并交换通过指令cmpxchg实现。

 

细节

在CAS中,有这样三个值:

V:要更新的变量(var)

E:预期值(expected) —— 本质上指的是“旧值”

N:新值(new) 

⽐较并交换的过程如下: 

判断V是否等于E,如果等于,将V的值设置为N;如果不等,说明已经有其它线程更新了V,则当前线程放弃更新,什么都不做。

 

当多个线程同时使⽤CAS操作⼀个变量时,只有⼀个会胜出,并成功更新,其余均会失败,但失败的线程并不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。

 

ABA 问题

关于CAS操作有个经典的ABA问题,具体案例如下:

线程I在获取变量X的值(变量X的初始值是A)后使用CAS操作将其修改为 B,假设操作成功,则程序运行一定是正确的吗?

其实未必,在线程I获取变量 X 的值后,在执行 CAS 前,线程II使用 CAS 修改变量X的值为B,随后又使用CAS修改变量X的值为A;则该场景中线程I在执行CAS时X的值虽然是A,但这个A己经不是线程I获取的A了。

 

ABA 问题的产生是因为变量的状态值产生了环形转换,即变量的值从 A 到 B, 然后再从 B 到 A。

如果变量的值只能朝着一个方向转换,比如 A 到 B,B 到 C,不构成环形,则不会存在问题。

ABA问题的解决思路是在变量前⾯追加上版本号或者时间戳。

 

从JDK 1.5开始,JDK的atomic包⾥提供了⼀个类 AtomicStampedReference 类来解决ABA问题:AtomicStampedReference 类给每个变量的状态值都配备了一个时间戳,从而避免了ABA问题的产生。

这个类的 compareAndSet ⽅法的作⽤是⾸先检查当前引⽤是否等于预期引⽤,并且 检查当前标志是否等于预期标志,如果⼆者都相等,才使⽤CAS设置为新的值和标志。 

    /**
     * Atomically sets the value of both the reference and stamp
     * to the given update values if the
     * current reference is {@code ==} to the expected reference
     * and the current stamp is equal to the expected stamp.
     *
     * @param expectedReference the expected value of the reference
     * @param newReference the new value for the reference
     * @param expectedStamp the expected value of the stamp
     * @param newStamp the new value for the stamp
     * @return {@code true} if successful
     */
    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)));
    }

 

问题

Java是怎样使⽤CAS的呢?

 

Unsafe

JDK 里的 Unsafe 类提供了一系列的 compareAndSwap*方法,代码文件路径如下: 

其定义了如下几个 compareAndSwap*方法:

剖析compareAndSwapInt

相关代码定义如下:

    /**
     * Atomically updates Java variable to {@code x} if it is currently
     * holding {@code expected}.
     *
     * <p>This operation has memory semantics of a {@code volatile} read
     * and write.  Corresponds to C11 atomic_compare_exchange_strong.
     *
     * @return {@code true} if successful
     */
    @ForceInline
    public final boolean compareAndSwapInt(Object o, long offset,
                                           int expected,
                                           int x) {
        return theInternalUnsafe.compareAndSetInt(o, offset, expected, x);
    }

从上述代码可以看到CAS有四个操作数,分别是:对象内存位置、对象中的变量的偏移量、变量预期值和新值。

具体含义:

如果对象 obj 中内存偏移量为 valueOffset的变量的值为 expect,则使用新值 update替换旧值 expect;这是处理器提供的一个原子性指令。

Note:

从上述方法的定义可以发现其实际上是调用了变量 theInternalUnsafe 的 compareAndSetInt 方法,该变量在类中的定义如下:

/**
 * A collection of methods for performing low-level, unsafe operations.
 * Although the class and all methods are public, use of this class is
 * limited because only trusted code can obtain instances of it.
 *
 * <em>Note:</em> It is the resposibility of the caller to make sure
 * arguments are checked before methods of this class are
 * called. While some rudimentary checks are performed on the input,
 * the checks are best effort and when performance is an overriding
 * priority, as when methods of this class are optimized by the
 * runtime compiler, some or all checks (if any) may be elided. Hence,
 * the caller must not rely on the checks and corresponding
 * exceptions!
 *
 * @author John R. Rose
 * @see #getUnsafe
 */

public final class Unsafe {

    static {
        Reflection.registerMethodsToFilter(Unsafe.class, "getUnsafe");
    }

    private Unsafe() {}

    private static final Unsafe theUnsafe = new Unsafe();
    private static final jdk.internal.misc.Unsafe theInternalUnsafe = jdk.internal.misc.Unsafe.getUnsafe();

    /**
     * Provides the caller with the capability of performing unsafe
     * operations.
     *
     * <p>The returned {@code Unsafe} object should be carefully guarded
     * by the caller, since it can be used to read and write data at arbitrary
     * memory addresses.  It must never be passed to untrusted code.
     *
     * <p>Most methods in this class are very low-level, and correspond to a
     * small number of hardware instructions (on typical machines).  Compilers
     * are encouraged to optimize these methods accordingly.
     *
     * <p>Here is a suggested idiom for using unsafe operations:
     *
     * <pre> {@code
     * class MyTrustedClass {
     *   private static final Unsafe unsafe = Unsafe.getUnsafe();
     *   ...
     *   private long myCountAddress = ...;
     *   public int getCount() { return unsafe.getByte(myCountAddress); }
     * }}</pre>
     *
     * (It may assist compilers to make the local variable {@code final}.)
     *
     * @throws  SecurityException if the class loader of the caller
     *          class is not in the system domain in which all permissions
     *          are granted.
     */
    @CallerSensitive
    public static Unsafe getUnsafe() {
        Class<?> caller = Reflection.getCallerClass();
        if (!VM.isSystemDomainLoader(caller.getClassLoader()))
            throw new SecurityException("Unsafe");
        return theUnsafe;
    }

 

theInternalUnsafe

前述 compareAndSwapInt 方法中使用的 theInternalUnsafe 也是Unsafe类的实例,由此可知JDK里还存在另一个 Unsafe 类,代码文件路径如下:

 

这两个Unsafe类对应的包名如下图所示

 

由JDK11中AtomicInteger的定义,知其使用的是jdk.internal.misc.Unsafe。

(JDK8中使用的是sun.misc.Unsafe,地址

AtomicInteger的代码片段如下:

/**
 * An {@code int} value that may be updated atomically.  See the
 * {@link VarHandle} specification for descriptions of the properties
 * of atomic accesses. An {@code AtomicInteger} is used in
 * applications such as atomically incremented counters, and cannot be
 * used as a replacement for an {@link java.lang.Integer}. However,
 * this class does extend {@code Number} to allow uniform access by
 * tools and utilities that deal with numerically-based classes.
 *
 * @since 1.5
 * @author Doug Lea
 */
public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    /*
     * This class intended to be implemented using VarHandles, but there
     * are unresolved cyclic startup dependencies.
     */
    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
    private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");

    private volatile int value;

    /**
     * Creates a new AtomicInteger with the given initial value.
     *
     * @param initialValue the initial value
     */
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    /**
     * Creates a new AtomicInteger with initial value {@code 0}.
     */
    public AtomicInteger() {
    }

 

jdk.internal.misc.Unsafe对应的代码片段如下:

/**
 * A collection of methods for performing low-level, unsafe operations.
 * Although the class and all methods are public, use of this class is
 * limited because only trusted code can obtain instances of it.
 *
 * <em>Note:</em> It is the resposibility of the caller to make sure
 * arguments are checked before methods of this class are
 * called. While some rudimentary checks are performed on the input,
 * the checks are best effort and when performance is an overriding
 * priority, as when methods of this class are optimized by the
 * runtime compiler, some or all checks (if any) may be elided. Hence,
 * the caller must not rely on the checks and corresponding
 * exceptions!
 *
 * @author John R. Rose
 * @see #getUnsafe
 */

public final class Unsafe {

    private static native void registerNatives();
    static {
        registerNatives();
    }

    private Unsafe() {}

    private static final Unsafe theUnsafe = new Unsafe();

    /**
     * Provides the caller with the capability of performing unsafe
     * operations.
     *
     * <p>The returned {@code Unsafe} object should be carefully guarded
     * by the caller, since it can be used to read and write data at arbitrary
     * memory addresses.  It must never be passed to untrusted code.
     *
     * <p>Most methods in this class are very low-level, and correspond to a
     * small number of hardware instructions (on typical machines).  Compilers
     * are encouraged to optimize these methods accordingly.
     *
     * <p>Here is a suggested idiom for using unsafe operations:
     *
     * <pre> {@code
     * class MyTrustedClass {
     *   private static final Unsafe unsafe = Unsafe.getUnsafe();
     *   ...
     *   private long myCountAddress = ...;
     *   public int getCount() { return unsafe.getByte(myCountAddress); }
     * }}</pre>
     *
     * (It may assist compilers to make the local variable {@code final}.)
     */
    public static Unsafe getUnsafe() {
        return theUnsafe;
    }

 

该类中有很多CompareAndSetXXX方法

 

剖析compareAndSetInt

    /**
     * Atomically updates Java variable to {@code x} if it is currently
     * holding {@code expected}.
     *
     * <p>This operation has memory semantics of a {@code volatile} read
     * and write.  Corresponds to C11 atomic_compare_exchange_strong.
     *
     * @return {@code true} if successful
     */
    @HotSpotIntrinsicCandidate
    public final native boolean compareAndSetInt(Object o, long offset,
                                                 int expected,
                                                 int x);

    @HotSpotIntrinsicCandidate
    public final native int compareAndExchangeInt(Object o, long offset,
                                                  int expected,
                                                  int x);

    @HotSpotIntrinsicCandidate
    public final int compareAndExchangeIntAcquire(Object o, long offset,
                                                         int expected,
                                                         int x) {
        return compareAndExchangeInt(o, offset, expected, x);
    }

    @HotSpotIntrinsicCandidate
    public final int compareAndExchangeIntRelease(Object o, long offset,
                                                         int expected,
                                                         int x) {
        return compareAndExchangeInt(o, offset, expected, x);
    }

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

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

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

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

从上述代码可以看到该Unsafe类中的compareAndSetInt 和 compareAndExchangeInt方法都是 native方法,它们使用 JNI 的方式访问本地C++实现库(它的具体实现和操作系统、CPU都有关系)。

 

参考资料:

https://howtodoinjava.com/java/multi-threading/compare-and-swap-cas-algorithm/

posted @ 2021-08-10 08:48  时空穿越者  阅读(248)  评论(0)    收藏  举报