锁机制实现详解
锁的分类体系
| 分类维度 | 锁类型 | 说明 |
|---|---|---|
| 按操作类型 | 读锁(共享锁) | 多个事务可同时持有,用于读取 |
| 写锁(排他锁) | 同一时间只能一个事务持有,用于写入 | |
| 按锁定范围 | 行锁 | 只锁定一行数据 |
| 表锁 | 锁定整个表 | |
| 页锁 | 锁定数据页(介于行锁和表锁之间) | |
| 按乐观程度 | 悲观锁 | 假设冲突会发生,先加锁 |
| 乐观锁 | 假设冲突少,更新时检查 |
读写锁(ReadWriteLock)
说明:Java 的
ReadWriteLock是 JVM 级别的并发控制工具,与数据库完全无关。它属于java.util.concurrent.locks包,用于在单进程内实现细粒度的读写分离控制。
ReadWriteLock 接口概述
ReadWriteLock 是 Java 并发包提供的接口,定义了一对锁:
- 读锁(共享锁):多个线程可同时获取,适合读多写少场景
- 写锁(排他锁):同一时间只能被一个线程持有
核心特性:
- 读写分离:读操作不阻塞其他读操作,提高并发读性能
- 写优先/读优先:不同实现有不同的锁获取策略
- 可重入性:
ReentrantReadWriteLock支持锁的重入
Java 读写锁实现
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Cache {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private Map<String, Object> cache = new HashMap<>();
public Object get(String key) {
rwLock.readLock().lock();
try {
return cache.get(key);
} finally {
rwLock.readLock().unlock();
}
}
public void put(String key, Object value) {
rwLock.writeLock().lock();
try {
cache.put(key, value);
} finally {
rwLock.writeLock().unlock();
}
}
}读写锁特性
| 场景 | 读锁 | 写锁 |
|---|---|---|
| 多个读锁 | 允许 | 阻塞 |
| 读锁+写锁 | 阻塞 | 阻塞 |
| 多个写锁 | 阻塞 | 阻塞 |
五、分布式锁实现
5.1 Redis 分布式锁
public class RedisDistributedLock {
private final StringRedisTemplate redisTemplate;
private static final String LOCK_PREFIX = "lock:";
private static final long DEFAULT_EXPIRE = 30000;
public boolean tryLock(String key, long expire) {
String lockKey = LOCK_PREFIX + key;
Boolean result = redisTemplate.opsForValue()
.setIfAbsent(lockKey, UUID.randomUUID().toString(), expire, TimeUnit.MILLISECONDS);
return Boolean.TRUE.equals(result);
}
public void unlock(String key) {
String lockKey = LOCK_PREFIX + key;
redisTemplate.delete(lockKey);
}
}5.2 Zookeeper 分布式锁
// Curator 框架实现
public class ZkDistributedLock {
private final CuratorFramework client;
private static final String LOCK_PATH = "/locks/";
public void acquire(String lockName) throws Exception {
String lockPath = LOCK_PATH + lockName;
InterProcessMutex mutex = new InterProcessMutex(client, lockPath);
mutex.acquire();
}
public void release(String lockName) throws Exception {
String lockPath = LOCK_PATH + lockName;
InterProcessMutex mutex = new InterProcessMutex(client, lockPath);
mutex.release();
}
}六、锁机制对比总结
| 锁类型 | 实现方式 | 并发性能 | 适用场景 |
|---|---|---|---|
| 悲观锁 | SELECT FOR UPDATE | 低 | 写多读少、库存扣减 |
| 乐观锁 | 版本号/CAS | 高 | 读多写少、普通更新 |
| 读写锁 | ReadWriteLock | 中高 | 缓存读写分离 |
| 分布式锁 | Redis/ZK | 中 | 跨进程/跨机器 |
七、实际业务场景示例
场景1:库存扣减(悲观锁)
@Transactional
public boolean purchase(Long productId, int quantity) {
Stock stock = stockRepository.findByProductIdForUpdate(productId);
if (stock.getCount() < quantity) {
throw new BusinessException("库存不足");
}
stock.setCount(stock.getCount() - quantity);
stockRepository.save(stock);
orderService.createOrder(productId, quantity);
return true;
}场景2:用户积分更新(乐观锁)
@Transactional
public boolean updatePoints(Long userId, int points) {
int retryCount = 3;
while (retryCount > 0) {
User user = userRepository.findById(userId).orElse(null);
int currentPoints = user.getPoints();
user.setPoints(currentPoints + points);
try {
userRepository.save(user);
return true;
} catch (OptimisticLockingFailureException e) {
retryCount--;
if (retryCount == 0) {
throw new BusinessException("更新失败,请重试");
}
}
}
return false;
}