VoucherOrderServiceImpl 在扣减库存时,改为:
boolean success = seckillVoucherService.update()
.setSql("stock= stock -1") //set stock = stock -1
.eq("voucher_id", voucherId).eq("stock",voucher.getStock()).update(); //where id = ? and stock = ?
以上逻辑的核心含义是:只要我扣减库存时的库存和之前我查询到的库存是一样的,就意味着没有人在中间修改过库存,那么此时就是安全的,但是以上这种方式通过测试发现会有很多失败的情况,失败的原因在于:在使用乐观锁过程中假设100个线程同时都拿到了100的库存,然后大家一起去进行扣减,但是100个人中只有1个人能扣减成功,其他的人在处理时,他们在扣减时,库存已经被修改过了,所以此时其他线程都会失败
之前的方式要修改前后都保持一致,但是这样我们分析过,成功的概率太低,所以我们的乐观锁需要变一下,改成stock大于0 即可
boolean success = seckillVoucherService.update()
.setSql("stock= stock -1")
.eq("voucher_id", voucherId).update().gt("stock",0); //where id = ? and stock > 0
针对cas中的自旋压力过大,我们可以使用Longaddr这个类去解决
Java8 提供的一个对AtomicLong改进后的一个类,LongAdder
大量线程并发更新一个原子性的时候,天然的问题就是自旋,会导致并发性问题,当然这也比我们直接使用syn来的好
所以利用这么一个类,LongAdder来进行优化
如果获取某个值,则会对cell和base的值进行递增,最后返回一个完整的值
乐观锁(Optimistic Locking)是一种在并发编程中常用的锁机制,它基于一个假设:大多数情况下,数据在读取和修改的过程中不会发生冲突。因此,乐观锁不会在操作数据时直接加锁,而是通过某种机制(如版本号或条件判断)来检测冲突。如果检测到冲突,则会采取相应的措施(如重试或回滚)。
在乐观锁的实现方式中,CAS(Compare-And-Swap,比较并交换) 是一种非常重要的机制,广泛应用于无锁编程(Lock-Free Programming)中。
CAS是一种原子操作,它包含三个参数:
CAS操作的逻辑如下:
CAS操作通常由硬件指令支持,确保操作的原子性,因此不会被其他线程中断。
在乐观锁中,CAS机制常用于实现无锁的并发控制。它通过版本号或直接操作内存值来检测冲突。以下是两种常见的应用场景:
在数据库或内存中为每个数据项维护一个版本号(version
字段)。每次更新数据时,版本号会递增。CAS机制用于确保在更新操作中,版本号没有被其他线程修改。
int currentVersion = getCurrentVersion();
int newVersion = currentVersion + 1;
if (compareAndSwapVersion(currentVersion, newVersion)) {
// 更新成功
updateData();
} else {
// 更新失败,重试或回滚
}
直接对内存值进行操作。例如,Java中的AtomicInteger
类使用CAS机制来实现线程安全的整数操作。
AtomicInteger counter = new AtomicInteger(0);
boolean success = counter.compareAndSet(0, 1); // 如果当前值为0,则更新为1
if (success) {
System.out.println("更新成功");
} else {
System.out.println("更新失败");
}
A
,线程B将值修改为B
,再修改回A
,线程A的CAS操作会成功,但实际上数据已经被修改过。Java提供了java.util.concurrent.atomic
包,其中包含了一系列基于CAS机制的原子类,如AtomicInteger
、AtomicLong
、AtomicReference
等。这些类通过CAS操作实现了线程安全的变量更新。
AtomicInteger
public class Counter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
// 使用CAS操作递增计数器
while (true) {
int current = count.get();
int next = current + 1;
if (count.compareAndSet(current, next)) {
break; // 更新成功,退出循环
}
}
}
public int get() {
return count.get();
}
}
可以通过引入版本号或使用AtomicStampedReference
来解决ABA问题。AtomicStampedReference
为每个值维护一个版本号(戳),确保即使值被修改多次,版本号也会递增。
AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(0, 0);
int[] stampHolder = new int[1];
int current = ref.get(stampHolder);
int stamp = stampHolder[0];
if (ref.compareAndSet(current, 1, stamp, stamp + 1)) {
System.out.println("更新成功");
} else {
System.out.println("更新失败");
}
在秒杀系统中,CAS机制可以用于实现乐观锁,确保库存扣减操作的线程安全性。例如:
public boolean deductStock(int voucherId) {
SeckillVoucher voucher = getSeckillVoucher(voucherId);
int currentStock = voucher.getStock();
if (currentStock <= 0) {
return false; // 库存不足
}
// 使用CAS机制扣减库存
if (compareAndSwapStock(voucherId, currentStock, currentStock - 1)) {
return true; // 扣减成功
} else {
return false; // 扣减失败,重试或返回失败
}
}
通过这种方式,CAS机制可以在高并发场景下有效减少锁的竞争,同时确保操作的原子性。
CAS机制是乐观锁的一种重要实现方式,适用于冲突概率较低的场景。它通过原子操作确保数据的一致性,同时避免了锁的开销,提高了并发性能。然而,CAS也存在ABA问题和高冲突场景下的性能瓶颈。在实际应用中,可以根据具体需求选择合适的锁机制,或结合其他技术(如版本号、退避算法)来优化性能。