使用CAS操作实现乐观锁的完整指南

乐观锁是一种高效的并发控制机制,而CAS(Compare-And-Swap)是实现乐观锁的核心技术。下面我将详细介绍如何通过CAS操作实现乐观锁。

一、CAS操作原理

CAS(Compare-And-Swap)是一种原子操作,包含三个操作数:

  1. 内存位置(V)
  2. 预期原值(A)
  3. 新值(B)

当且仅当V的值等于A时,CAS才会将V的值更新为B,否则不做任何操作。无论是否更新成功,CAS都会返回V的当前值。

二、Java中的CAS支持

Java通过java.util.concurrent.atomic包提供了CAS支持:

java 复制代码
// AtomicInteger的CAS实现示例
public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

三、基于CAS实现乐观锁

1. 简单计数器实现

java 复制代码
public class OptimisticLockCounter {
    private AtomicInteger count = new AtomicInteger(0);
    
    public void increment() {
        int oldValue;
        int newValue;
        do {
            oldValue = count.get();      // 读取当前值
            newValue = oldValue + 1;     // 计算新值
        } while (!count.compareAndSet(oldValue, newValue)); // CAS更新
    }
    
    public int getCount() {
        return count.get();
    }
}

2. 通用对象乐观锁实现

java 复制代码
public class OptimisticLock<T> {
    private AtomicReference<T> valueRef;
    private AtomicInteger version = new AtomicInteger(0);
    
    public OptimisticLock(T initialValue) {
        this.valueRef = new AtomicReference<>(initialValue);
    }
    
    public void update(UnaryOperator<T> updateFunction) {
        T oldValue;
        T newValue;
        int oldVersion;
        int newVersion;
        do {
            oldValue = valueRef.get();
            oldVersion = version.get();
            newValue = updateFunction.apply(oldValue);
            newVersion = oldVersion + 1;
        } while (!(valueRef.compareAndSet(oldValue, newValue) && 
                  version.compareAndSet(oldVersion, newVersion)));
    }
    
    public T getValue() {
        return valueRef.get();
    }
}

四、CAS乐观锁的典型应用

1. 无锁栈实现

java 复制代码
public class ConcurrentStack<E> {
    private AtomicReference<Node<E>> top = new AtomicReference<>();
    
    public void push(E item) {
        Node<E> newHead = new Node<>(item);
        Node<E> oldHead;
        do {
            oldHead = top.get();
            newHead.next = oldHead;
        } while (!top.compareAndSet(oldHead, newHead));
    }
    
    public E pop() {
        Node<E> oldHead;
        Node<E> newHead;
        do {
            oldHead = top.get();
            if (oldHead == null) {
                return null;
            }
            newHead = oldHead.next;
        } while (!top.compareAndSet(oldHead, newHead));
        return oldHead.item;
    }
    
    private static class Node<E> {
        final E item;
        Node<E> next;
        
        Node(E item) {
            this.item = item;
        }
    }
}

2. 账户余额安全更新

java 复制代码
public class BankAccount {
    private AtomicInteger balance;
    private AtomicInteger version = new AtomicInteger(0);
    
    public BankAccount(int initialBalance) {
        this.balance = new AtomicInteger(initialBalance);
    }
    
    public boolean transfer(int amount) {
        int currentBalance;
        int newBalance;
        int currentVersion;
        int newVersion;
        do {
            currentBalance = balance.get();
            currentVersion = version.get();
            if (currentBalance + amount < 0) { // 余额不足检查
                return false;
            }
            newBalance = currentBalance + amount;
            newVersion = currentVersion + 1;
        } while (!(balance.compareAndSet(currentBalance, newBalance) && 
                  version.compareAndSet(currentVersion, newVersion)));
        return true;
    }
    
    public int getBalance() {
        return balance.get();
    }
}

五、CAS乐观锁的优化技巧

1. 指数退避策略:减少高竞争下的CPU消耗

java 复制代码
public boolean transferWithBackoff(int amount) {
    int retries = 0;
    final int MAX_RETRIES = 10;
    final long BASE_DELAY_MS = 10;
    
    while (retries < MAX_RETRIES) {
        int currentBalance = balance.get();
        int currentVersion = version.get();
        
        if (currentBalance + amount < 0) {
            return false;
        }
        
        if (balance.compareAndSet(currentBalance, currentBalance + amount) &&
            version.compareAndSet(currentVersion, currentVersion + 1)) {
            return true;
        }
        
        // 指数退避
        try {
            long delay = (long) (BASE_DELAY_MS * Math.pow(2, retries));
            Thread.sleep(delay);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }
        
        retries++;
    }
    return false;
}

2. 版本号压缩:将版本号和值打包到一个long中

java 复制代码
public class CompactOptimisticLock {
    private static final int VERSION_BITS = 32;
    private AtomicLong state;
    
    public CompactOptimisticLock(int initialValue) {
        this.state = new AtomicLong(((long)initialValue << VERSION_BITS) | 0L);
    }
    
    public void update(UnaryOperator<Integer> updateFunction) {
        long oldState;
        long newState;
        int oldValue;
        int newValue;
        int oldVersion;
        int newVersion;
        
        do {
            oldState = state.get();
            oldValue = (int)(oldState >>> VERSION_BITS);
            oldVersion = (int)(oldState & 0xFFFFFFFFL);
            newValue = updateFunction.apply(oldValue);
            newVersion = oldVersion + 1;
            newState = ((long)newValue << VERSION_BITS) | newVersion;
        } while (!state.compareAndSet(oldState, newState));
    }
    
    public int getValue() {
        return (int)(state.get() >>> VERSION_BITS);
    }
}

六、CAS乐观锁的局限性及解决方案

  1. ABA问题

    • 问题描述:值从A变为B又变回A,CAS无法检测到中间变化

    • 解决方案:使用AtomicStampedReferenceAtomicMarkableReference

java 复制代码
// 使用AtomicStampedReference解决ABA问题
public class ABASafeStack<E> {
    private AtomicStampedReference<Node<E>> top = 
        new AtomicStampedReference<>(null, 0);
    
    public void push(E item) {
        Node<E> newHead = new Node<>(item);
        int[] stampHolder = new int[1];
        Node<E> oldHead;
        int oldStamp;
        do {
            oldHead = top.get(stampHolder);
            oldStamp = stampHolder[0];
            newHead.next = oldHead;
        } while (!top.compareAndSet(oldHead, newHead, oldStamp, oldStamp + 1));
    }
    
    public E pop() {
        int[] stampHolder = new int[1];
        Node<E> oldHead;
        Node<E> newHead;
        int oldStamp;
        do {
            oldHead = top.get(stampHolder);
            oldStamp = stampHolder[0];
            if (oldHead == null) {
                return null;
            }
            newHead = oldHead.next;
        } while (!top.compareAndSet(oldHead, newHead, oldStamp, oldStamp + 1));
        return oldHead.item;
    }
}

2. 循环时间长开销大

• 问题描述:高竞争下CAS可能长时间自旋

• 解决方案:结合线程让步或系统调度

java 复制代码
public boolean transferWithYield(int amount) {
    int currentBalance;
    int newBalance;
    int currentVersion;
    int newVersion;
    int spins = 0;
    final int YIELD_THRESHOLD = 10;
    
    do {
        if (spins++ > YIELD_THRESHOLD) {
            Thread.yield(); // 让出CPU时间片
            spins = 0;
        }
        
        currentBalance = balance.get();
        currentVersion = version.get();
        
        if (currentBalance + amount < 0) {
            return false;
        }
        
        newBalance = currentBalance + amount;
        newVersion = currentVersion + 1;
    } while (!(balance.compareAndSet(currentBalance, newBalance) && 
              version.compareAndSet(currentVersion, newVersion)));
    
    return true;
}

七、CAS乐观锁与数据库乐观锁对比

特性 CAS乐观锁 数据库乐观锁
作用范围 单个JVM进程内 跨进程、分布式环境
实现复杂度 相对简单 需要数据库支持
性能 极高(纳秒级) 较高(微秒级)
持久性 不持久 持久
ABA问题 存在 不存在
适用场景 内存数据结构、高并发计数器 分布式系统、数据库并发控制

八、总结

CAS操作是实现乐观锁的高效方式,具有以下特点:

  1. 无锁:避免线程阻塞和上下文切换
  2. 高性能:适合高并发场景
  3. 可扩展:可用于构建各种并发数据结构

在实际应用中,需要根据具体场景:

• 处理ABA问题

• 优化自旋策略

• 结合版本控制

• 必要时退化为悲观锁

通过合理使用CAS乐观锁,可以显著提高Java应用的并发性能和吞吐量。

相关推荐
Bl_a_ck3 分钟前
【React】Craco 简介
开发语言·前端·react.js·typescript·前端框架
编程有点难7 分钟前
Python训练打卡Day23
开发语言·python
程序员爱钓鱼9 分钟前
跳转语句:break、continue、goto -《Go语言实战指南》
开发语言·后端·golang·go1.19
hardStudy_h16 分钟前
C程序的存储空间分配
c语言·开发语言
橙子1991101617 分钟前
Kotlin 中的 Unit 类型的作用以及 Java 中 Void 的区别
java·开发语言·kotlin
yours_Gabriel24 分钟前
【登录认证】JWT令牌
java·开发语言·redis
lyw20561937 分钟前
微服务八股(自用)
java·开发语言
dot to one38 分钟前
Qt 中 QWidget涉及的常用核心属性介绍
开发语言·c++·qt
液态不合群1 小时前
理解 C# 中的各类指针
java·开发语言·c#
橙子199110161 小时前
Kotlin 中的作用域函数
android·开发语言·kotlin