CAS无锁算法

乐观锁 VS 悲观锁

乐观锁与悲观锁是一种广义上的概念,体现了看待线程同步的不同角度。在Java和数据库中都有此概念对应的实际应用。

先说概念。对于同一个数据的并发操作,悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。Java中,synchronized关键字和Lock的实现类都是悲观锁。

而乐观锁认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新,则根据不同的实现方式执行不同的操作(例如报错或者自动重试)。

乐观锁在Java中是通过使用无锁编程来实现,最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。

介绍

CAS全称 Compare And Swap(比较与交换),是一种无锁算法。在不使用锁(没有线程被阻塞)的情况下实现多线程之间的变量同步。java.util.concurrent包中的原子类就是通过CAS来实现了乐观锁。

CAS算法涉及到三个操作数:

  • 需要读写的内存值 V。
  • 进行比较的值 A。
  • 要写入的新值 B。

当且仅当 V 的值等于 A 时,CAS通过原子方式用新值B来更新V的值(“比较+更新”整体是一个原子操作),否则不会执行任何操作。一般情况下,“更新”是一个不断重试的操作。

存在问题

  • ABA问题。CAS需要在操作值的时候检查内存值是否发生变化,没有发生变化才会更新内存值。但是如果内存值原来是A,后来变成了B,然后又变成了A,那么CAS进行检查时会发现值没有发生变化,但是实际上是有变化的。ABA问题的解决思路就是在变量前面添加版本号,每次变量更新的时候都把版本号加一,这样变化过程就从“A-B-A”变成了“1A-2B-3A”。

JDK从1.5开始提供了AtomicStampedReference类来解决ABA问题,具体操作封装在compareAndSet()中。compareAndSet()首先检查当前引用和当前标志与预期引用和预期标志是否相等,如果都相等,则以原子方式将引用值和标志的值设置为给定的更新值。

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

private boolean casPair(Pair<V> cmp, Pair<V> val) {
    return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}
  • 循环时间长开销大。CAS操作如果长时间不成功,会导致其一直自旋,给CPU带来非常大的开销。

  • 只能保证一个共享变量的原子操作(解决方案AtomicReference 类)。对一个共享变量执行操作时,CAS能够保证原子操作,但是对多个共享变量操作时,CAS是无法保证操作的原子性的。

Java从1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,可以把多个变量放在一个对象里来进行CAS操作。

juca

AtomicInteger 整型原子类
AtomicReference 原子引用类 可以保证自定义对象多个属性的原子性
AtomicStampedReference 带时间戳的原子引用类 解决ABA问题


参考博客:

  1. https://blog.youkuaiyun.com/meituantech/article/details/84138163
### CAS队列的原理 CAS(Compare And Swap)是一种常见的原子操作,广泛应用于实现无数据结构。其核心思想是比较内存中的某个位置上的值是否等于预期值,如果相等,则将其替换为新值;如果不相等,则不做任何修改[^1]。这种机制确保了即使在多线程环境下也能安全地更新共享资源。 #### 无队列的核心概念 无队列的设计目标是在不依赖互斥的情况下完成入队和出队操作。它通过使用原子操作来协调多个线程之间的访问冲突,从而避免因竞争而导致的性能瓶颈[^3]。具体来说: - **入队操作**:当一个线程尝试向队列中添加元素时,会先获取当前尾指针的位置,并将新的节点附加到该位置上。随后,通过CAS指令更新尾指针指向新增加的节点。 - **出队操作**:对于移除元素的操作,线程需要找到头指针对应的节点,并通过CAS指令将其标记为空闲状态或者直接删除。之后调整头指针至下一个有效节点。 这些过程均需借助于底层硬件支持的原子级比较交换功能来保障一致性[^2]。 ### 实现细节分析 以下是基于单生产者/消费者模型的一个简单版本C++代码示例展示如何利用标准库提供的`std::atomic`类配合自定义逻辑构建基本形式下的无环形缓冲区(queue): ```cpp #include <iostream> #include <atomic> template<typename T, size_t N> class LockFreeQueue { private: struct Node { std::atomic<T*> value; bool used; Node() : value(nullptr), used(false) {} }; alignas(64) std::array<Node, N> buffer; // Cache line padding to prevent false sharing. std::atomic<size_t> head; std::atomic<size_t> tail; public: explicit LockFreeQueue():head{0},tail{0}{} ~LockFreeQueue(){ clear(); } void enqueue(T* item){ while(true){ auto t=tail.load(std::memory_order_acquire); const auto next=(t+1)%N; if(head.load(std::memory_order_relaxed)==t && !buffer[t].used){ if(tail.compare_exchange_weak(t,next,std::memory_order_release)){ buffer[next].value.store(item,std::memory_order_release); buffer[next].used=true; break; } }else{ // Queue is full or race condition occurred. continue; } } } T* dequeue(){ while(true){ auto h=head.load(std::memory_order_acquire); if(h==tail.load(std::memory_order_acquire))return nullptr;//queue empty. const auto index=h%N; if(buffer[index].used && (h==head.exchange((h+1)%N,std::memory_order_acq_rel))){ T *res=buffer[index].value.exchange(nullptr,std::memory_order_release); buffer[index].used=false; return res; } } } void clear(){ while(auto p=dequeue())delete p; } }; ``` 上述代码片段展示了如何创建一个固定大小的循环数组作为存储容器,并采用两个独立计数器分别追踪生产和消费进度。每次执行插入或提取动作前都会验证当前位置的状态以决定下一步行动方案,整个流程完全依靠CAS命令维持同步关系而不涉及显式的定机制。 ### 性能考量 相比传统的带方法,无算法减少了由于频繁请求获得独占权限所引发的时间消耗以及可能存在的死风险等问题。然而值得注意的是,在极高负载条件下也可能出现所谓的ABA问题——即某些特定序列变化可能导致误判情况发生,因此实际应用时常还需引入额外辅助手段加以防范。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值