Java 与 MySQL 中锁的使用与区别
在高并发场景下(比如商品库存扣减、账户余额扣减),并发控制是避免数据异常(如超卖、负库存)的关键手段。常见的实现方式主要有两类:
-
Java 层面的锁(synchronized、Lock)
-
数据库层面的锁(悲观锁、乐观锁)
-
分布式锁(Redis/Zookeeper)
下面结合实际案例(库存扣减),对这几种方式的实现和区别进行说明。
一、Java 层的锁
1.1 同步锁(synchronized)
synchronized 属于悲观锁,假设总是会有线程修改数据,所以每次访问都要加锁。
特点:
-
锁住的是 JVM 内存中的对象。
-
只能保证单机环境线程安全。
-
无法解决分布式系统中的数据一致性问题。
public synchronized boolean reduceWithSynchronized(String productName, Integer amount) {
Inventory inventory = inventoryRepository.findByProductName(productName)
.orElseThrow(() -> new RuntimeException("商品不存在"));
if (inventory.getTotalAmount() < amount) {
return false;
}
inventory.setTotalAmount(inventory.getTotalAmount() - amount);
inventoryRepository.save(inventory);
return true;
}
1.2 显式锁(Lock 接口)
Java 并发包(java.util.concurrent.locks)提供了更灵活的锁实现,典型的是 ReentrantLock。
-
ReentrantLock(可重入锁)-
支持尝试加锁
tryLock()。 -
支持超时加锁
tryLock(time, unit)。 -
可以选择公平锁/非公平锁。
-
提供条件变量
Condition,实现更精细的线程协作。
-
Lock lock = new ReentrantLock();
public void reduce() {
lock.lock();
try {
// 业务逻辑
} finally {
lock.unlock();
}
}
1.3 读写锁(ReadWriteLock)
-
典型实现:
ReentrantReadWriteLock。 -
特点:
-
多个读操作可以并发。
-
写操作独占。
-
-
适合 读多写少 的场景,比如缓存、配置管理。
1.4 原子类(CAS 乐观锁)
-
Java 并发包中的 原子类(AtomicInteger、AtomicLong、AtomicReference ...) 本质上是基于 CAS(Compare And Swap) 实现的乐观锁。
-
不加锁,但通过 CPU 指令级别的比较交换保证线程安全。
1.5 CAS 的执行过程(举例)
假设有一个 AtomicInteger count = new AtomicInteger(5),线程想把值从 5 改成 6。
-
线程 A 读取到 内存值 V=5。
-
线程 A 认为预期值 E=5,准备更新为新值 N=6。
-
CPU 执行
cmpxchg:-
如果内存中的值还是 5(V==E),则写入 6,返回 true。
-
如果内存中的值已变(可能被线程 B 改成了 7),则写入失败,返回 false。
-
-
如果失败,线程 A 会重新读取最新值,再尝试 CAS → 形成 自旋。
1.6 CAS 的问题
虽然 CAS 非常高效,但也有一些典型问题:
-
ABA 问题
-
变量值从 A → B → A,CAS 看到值没变,但实际发生过修改。
-
解决:
AtomicStampedReference(带版本号) 或LongAdder。
-
-
自旋开销大
-
如果竞争激烈,CAS 会不断重试,消耗 CPU。
-
JDK8 提供了
LongAdder等分段累加器,降低竞争。
-
-
只能保证一个变量的原子性
-
如果需要多个变量同时 CAS → 需要加锁,或者用
AtomicReference封装对象。
-
AtomicInteger stock = new AtomicInteger(100);
public boolean reduce(int amount) {
while (true) {
int current = stock.get();
if (current < amount) {
return false;
}
if (stock.compareAndSet(current, current - amount)) {
return true;
}
}
}
二、数据库层的锁
2.1 悲观锁(Pessimistic Lock)
在查询时直接加锁(如 select ... for update),阻塞其他事务的修改操作。
特点:
-
避免并发写冲突。
-
性能差:会阻塞其他事务。
-
适用于并发量不高、数据一致性要求严格的场景。
@Transactional(isolation = Isolation.READ_COMMITTED)
public boolean reduceWithPessimisticLock(String productName, Integer amount) {
Inventory inventory = inventoryRepository.findByProductNameForUpdate(productName)
.orElseThrow(() -> new RuntimeException("商品不存在"));
if (inventory.getTotalAmount() < amount) {
return false;
}
inventory.setTotalAmount(inventory.getTotalAmount() - amount);
inventoryRepository.save(inventory);
return true;
}
2.2 乐观锁(Optimistic Lock)
不加锁,读取时带上版本号(version),更新时判断版本号是否变化。
特点:
-
并发高时性能更好。
-
需要业务层处理失败的重试逻辑。
-
常见实现:数据库字段 version + CAS。
@Transactional
public boolean reduceWithOptimisticLock(String productName, Integer amount) {
Inventory inventory = inventoryRepository.findByProductName(productName)
.orElseThrow(() -> new RuntimeException("商品不存在"));
if (inventory.getTotalAmount() < amount) {
return false;
}
int updatedRows = inventoryRepository.updateInventoryWithOptimisticLock(
productName,
inventory.getTotalAmount() - amount,
inventory.getVersion()
);
if (updatedRows == 0) {
throw new RuntimeException("库存扣减失败,请重试");
}
return true;
}
三、分布式锁(Redis 实现)
在分布式环境下,单机 synchronized 或数据库行锁无法保证不同服务节点的安全,这时常用 Redis 分布式锁。
特点:
-
避免跨服务的数据冲突。
-
性能高,但需要注意锁过期、续命、原子性问题。
public boolean reduceWithRedisLock(String productName, Integer amount) {
String lockKey = "inventory_lock:" + productName;
String requestId = UUID.randomUUID().toString();
try {
if (!redisLockService.tryLock(lockKey, requestId, 30)) {
return false;
}
Inventory inventory = inventoryRepository.findByProductName(productName)
.orElseThrow(() -> new RuntimeException("商品不存在"));
if (inventory.getTotalAmount() < amount) {
return false;
}
inventory.setTotalAmount(inventory.getTotalAmount() - amount);
inventoryRepository.save(inventory);
return true;
} finally {
redisLockService.releaseLock(lockKey, requestId);
}
}
对比
| 方案 | 分类 | 优点 | 缺点 | 使用场景 |
|---|---|---|---|---|
| synchronized | Java 悲观锁 | 实现简单,线程安全 | 单机有效,无法分布式 | 单机小项目 |
| 数据库行锁 | 数据库悲观锁 | 简单可靠,事务内保证一致性 | 阻塞性能差,可能死锁 | 数据量小,写冲突少 |
| 乐观锁(version) | 数据库乐观锁 | 高并发下性能好 | 失败需要重试 | 互联网库存扣减、余额扣减 |
| Redis 分布式锁 | 分布式悲观锁 | 跨服务,性能较高 | 需要解决续命和原子性问题 | 分布式集群,高并发业务 |
更多推荐

所有评论(0)