乐观锁的实现之cas(Compare and Swap)

乐观锁的实现除了常用的版本号、时间戳、哈希值这些外,还可以用cas实现。

在Java中,java.util.concurrent.atomic包提供了一系列基于CAS操作的原子类,如AtomicIntegerAtomicLongAtomicReference等,用于实现线程安全的原子操作。

什么是cas操作

CAS(Compare and Swap)操作是一种并发编程中常用的原子操作,用于实现多线程环境下的无锁同步。CAS操作可以实现对共享变量的原子性读取和更新。

CAS操作包括三个操作数:内存地址(或者说是变量的引用)、当前期望的值和新值。CAS操作的执行过程如下:

1. 读取内存地址的当前值。

2. 检查当前值是否与期望的值相等。

3. 如果相等,将新值写入内存地址;否则,放弃操作。

在执行CAS操作时,如果当前值与期望的值相等,说明在读取到值后没有其他线程修改过该值。此时,CAS操作会原子地将新值写入内存地址。如果当前值与期望的值不相等,说明有其他线程已经修改了该值,CAS操作会放弃写入并重新尝试。

CAS操作是一种乐观的并发控制方式,它避免了传统的锁机制带来的竞争和阻塞。通过不使用锁,CAS操作可以提高并发性能,同时确保对共享变量的原子性操作。

在Java中,`java.util.concurrent.atomic`包提供了一系列基于CAS操作的原子类,如`AtomicInteger`、`AtomicLong`、`AtomicReference`等,用于实现线程安全的原子操作。

总结:

CAS(Compare and Swap)操作是一种并发编程中的原子操作,用于实现无锁同步。它通过比较当前值与期望值是否相等来决定是否更新共享变量的值。CAS操作是一种乐观的并发控制方式,避免了传统锁机制的竞争和阻塞。

例子

在Spring Boot框架中,可以使用AtomicInteger类来实现CAS操作,用于实现对库存数量的原子性更新。以下是一个简单示例:

import java.util.concurrent.atomic.AtomicInteger;

public class Inventory {
    private AtomicInteger count;

    public Inventory(int initialCount) {
        this.count = new AtomicInteger(initialCount);
    }

    public int getCount() {
        return count.get();
    }

    public boolean updateCount(int expected, int newValue) {
        return count.compareAndSet(expected, newValue);
    }
}

在上述示例中,Inventory类表示库存,使用AtomicInteger作为库存数量的原子变量。updateCount方法用于更新库存数量,使用compareAndSet方法进行CAS操作。

public class Main {
    public static void main(String[] args) {
        Inventory inventory = new Inventory(10);

        int expected = 10;
        int newValue = 5;
        boolean success = inventory.updateCount(expected, newValue);
        if (success) {
            System.out.println("库存更新成功");
        } else {
            System.out.println("库存更新失败");
        }
        
        System.out.println("当前库存数量:" + inventory.getCount());
    }
}

在上述示例中,我们创建了一个Inventory对象,并尝试将库存数量从10更新为5。使用updateCount方法进行CAS操作,如果CAS操作成功,意味着更新成功;否则,更新失败。

需要注意的是,CAS操作需要指定期望值(expected)和新值(newValue)。CAS操作会比较当前库存数量与期望值是否相等,如果相等,才会将新值写入。如果当前库存数量与期望值不相等,CAS操作会失败,更新不会进行。

问:

上面的写法,每个线程进来都会执行Inventory inventory = new Inventory(10),这样会存在多个库存inventory ,那岂不是会导致超卖?

答:

是的,在之前的示例中,每个线程都会创建一个新的Inventory对象,这会导致每个线程都有自己的库存实例,从而可能导致超卖或不一致的问题。

为了避免这个问题,我们需要保证所有线程共享同一个库存实例。在Spring Boot框架中,可以使用依赖注入(Dependency Injection)的方式来实现共享的库存实例。以下是修改后的示例:

import org.springframework.stereotype.Component;
import java.util.concurrent.atomic.AtomicInteger;

@Component
public class Inventory {
    private AtomicInteger count;

    public Inventory() {
        this.count = new AtomicInteger(10); // 初始化库存数量,实际开发中,可以从数据库或缓存获取数来源
    }

    public int getCount() {
        return count.get();
    }

    public boolean updateCount(int expected, int newValue) {
        return count.compareAndSet(expected, newValue);
    }
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import javax.annotation.PostConstruct;

@SpringBootApplication
public class Application {
    @Autowired
    private Inventory inventory;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @PostConstruct
    public void simulateInventoryUpdate() {
        // 获取库存数量
        int currentCount = inventory.getCount();
        System.out.println("当前库存数量:" + currentCount);

        // 假设多个线程进行库存更新
        // ...

        // 模拟更新库存
        int expected = currentCount;
        int newValue = currentCount - 2;
        boolean success = inventory.updateCount(expected, newValue);
        if (success) {
            System.out.println("库存更新成功");
        } else {
            System.out.println("库存更新失败");
        }

        // 更新后的库存数量
        int updatedCount = inventory.getCount();
        System.out.println("更新后的库存数量:" + updatedCount);
    }
}

在上述示例中,我们将Inventory类标记为@Component,以便在其他组件中注入共享的库存实例。通过依赖注入,所有组件将共享同一个Inventory对象。

当需要使用库存的地方,可以通过构造函数注入或使用@Autowired注解来获取共享的库存实例。

这样,多个线程同时操作库存时,它们都会使用同一个库存实例,避免了多个实例导致的超卖或不一致问题。

在上述示例代码中,我们创建了一个Application类作为Spring Boot应用的入口类。通过构造函数注入或使用@Autowired注解,我们将Inventory库存实例注入到Application类中。

simulateInventoryUpdate方法中,我们模拟了多个线程进行库存更新的场景。首先获取当前库存数量,然后尝试进行库存更新操作,并打印更新后的库存数量。

通过Spring的依赖注入机制,我们可以确保多个线程共享同一个库存实例,避免了多个实例导致的超卖或不一致问题。

菜鸟学习笔记,不对请指正!

  • 23
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值