乐观锁的实现除了常用的版本号、时间戳、哈希值这些外,还可以用cas实现。
在Java中,java.util.concurrent.atomic
包提供了一系列基于CAS操作的原子类,如AtomicInteger
、AtomicLong
、AtomicReference
等,用于实现线程安全的原子操作。
什么是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的依赖注入机制,我们可以确保多个线程共享同一个库存实例,避免了多个实例导致的超卖或不一致问题。
菜鸟学习笔记,不对请指正!