【JUC-CAS】原子类基础

CAS是什么

CAS (Compare And Set),比较并交换,工作流程就是比较内存中的值与我现在获取的是否一致,如果一致才做修改,否则什么也不做。

CAS操作主要是依赖于汇编的一个指令:cmpxchg,也就是比较并交换。

Q:CAS保持原子性的关键是什么?
A:cmpxchg本身并不保证原子性,但是当有多个核的时候,执行cmpxchg的时候会锁住总线,保证只有一个CPU能在总线上进行数据传输,从而保证了原子性。

由于Java本身并不能控制底层操作系统,那么Java是如何实现CAS的呢?

Java设计者考虑到这一点,提供了一系列native方法,链接到cpp编写的代码,从而调用底层操作系统。

Java中一部分操作底层资源的native方法放在了Unsafe类中,由于操作的是底层资源,是不安全的,所以类也命名成unsafe

Unsafe类

类方法注释:

用于执行低级、不安全操作的方法集合。尽管该类和所有方法都是公共的,但该类的使用受到限制,因为只有受信任的代码才能获取该类的实例。注意:调用方有责任确保在调用此类的方法之前检查参数。虽然对 input 执行了一些基本检查,但这些检查是尽力而为的,并且当性能是压倒一切的优先级时,例如当运行时编译器优化此类的方法时,可能会省略部分或全部检查(如果有)。因此,调用方不得依赖检查和相应的异常!

因此,不建议大家调用Unsafe类!可以看一下都有什么

	// CAS
    @IntrinsicCandidate
    public final native boolean compareAndSetInt(Object o, long offset,
                                                 int expected,
                                                 int x);

    @IntrinsicCandidate
    public final native int compareAndExchangeInt(Object o, long offset,
                                                  int expected,
                                                  int x);
// 内存屏障
@IntrinsicCandidate
public native void loadFence();
@IntrinsicCandidate
public native void storeFence();
@IntrinsicCandidate
public native void fullFence();

看一下compareAndSetInt底层是什么实现的,在Java源码中/jdk17-master/src/hotspot/share/prims/unsafe.cpp

UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) {
  oop p = JNIHandles::resolve(obj);
  if (p == NULL) {
  	// 找到对象对应的字段
    volatile jint* addr = (volatile jint*)index_oop_from_field_offset_long(p, offset);
    return RawAccess<>::atomic_cmpxchg(addr, e, x) == e;
  } else {
    assert_field_offset_sane(p, offset);
    return HeapAccess<>::atomic_cmpxchg_at(p, (ptrdiff_t)offset, e, x) == e;
  }
} UNSAFE_END

可以看到,不管哪个分支,最后都走了atomic_cmpxchg_at这个方法,这个方法名要敏感,包含了cmpxchg,就是调用了底层的指令,并对总线加锁保证原子性。

自旋锁

自旋锁其实采用了CAS的思想,不对共享资源加锁,如果内存中的值不是我手中的值,我就把内存中的值拿过来,继续比较,如果是,做交换。

unsafe.java中有体现:

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;
}

CAS的缺点

CAS的缺点有两个:
第一个很明显,while循环会导致CPU空转,浪费资源。
第二个就是会出现ABA问题。

ABA问题就是一个线程对内存做了修改,再改回去,另一个线程的视角看什么都没变化。
具体来说,假如内存中有一个值x=1,A线程在自旋的时候,拿到了v=x=1,此时发生了线程切换,线程B将内存中的值由1变成了2,再变了回来。
A线程切换回来的时候,执行weakCompareAndSetInt成功,对A来说,它不知道已经被人动过了。

改进

加个版本号就好了:AtomicStampedReference能解决这一点,具体原子类使用看下一章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值