摘要:凌晨3点的办公室,我盯着屏幕上诡异的余额数据——明明有100个并发扣款请求,账户却只扣减了87次。这个血淋淋的教训让我真正读懂了锁机制的精髓。本文将用真实案例拆解两种锁的底层实现,带你避开千万级并发场景中的深坑。
凌晨3点的办公室,我盯着屏幕上诡异的余额数据——明明有100个并发扣款请求,账户却只扣减了87次。这个血淋淋的教训让我真正读懂了锁机制的精髓。本文将用真实案例拆解两种锁的底层实现,带你避开千万级并发场景中的深坑。
数据库级:BEGIN;SELECT * FROM account WHERE id=1 FOR UPDATE; -- 加行锁UPDATE account SET balance=balance-100 WHERE id=1;COMMIT;java同步锁:java
// 悲观锁经典实现public synchronized void transfer(Long from, Long to, BigDecimal amount) { // 业务逻辑}版本号方案:UPDATE products SET stock=stock-1, version=version+1 WHERE id=1001 AND version=3; -- 版本号校验CAS原子操作:java
AtomicInteger atomicInt = new AtomicInteger(5);atomicInt.compareAndSet(5, 10); // 核心CAS操作悲观锁方案:
java
@Transactionalpublic boolean deductStock(Long itemId) { // 1. 查询并加锁 Item item = itemMapper.selectForUpdate(itemId); // 2. 校验库存 if(item.getStock乐观锁方案:
java
public boolean deductStock(Long itemId) { while(true) { // 1. 获取当前版本 Item item = itemMapper.selectById(itemId); // 2. 校验库存 if(item.getStock 0) return true; }}ABA问题(数据被改回原值)java
// 解决方案:增加时间戳版本号AtomicStampedReference atomicRef = new AtomicStampedReference(0, 0);自旋消耗CPU(高并发场景循环重试代价大)版本号竞争激烈导致成功率下降来源:大龄程序猿小武