前言
在项目中我们经常需要进行扣减库存,或者防止并发操作,本文以redis为基础简单实现防止并发。
一、RedisLock工具类
RedisLock工具类
- tryLock方法,返回boolean值,如果未true说明之前没有锁,加锁成功,false说明之前的锁还未释放。
- unlock方法,删除锁
@Component
public class RedisLock {
private static final String LOCK_KEY_PREFIX = "LOCK:";
@Resource
private RedisTemplate<String, String> redisTemplate;
/**
*
* @param key
* @param second 锁时间
* @param waitLockSecond 获取锁失败时等待的时间
* @return
*/
public boolean tryLock(String key, int second, int waitLockSecond) {
long beginTime = System.currentTimeMillis();
try {
while(true) {
Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(buildLockKey(key), key, second, TimeUnit.SECONDS);
if(Boolean.TRUE.equals(aBoolean)) {
return true;
}
if(System.currentTimeMillis() > beginTime + waitLockSecond * 1000) {
return false;
}
// 休眠
TimeUnit.MILLISECONDS.sleep(250L);
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
private static String buildLockKey(String key) {
return LOCK_KEY_PREFIX + key;
}
public void unlock(String key) {
redisTemplate.delete(buildLockKey(key));
}
public void reLock(String key, int second){
redisTemplate.opsForValue().set(buildLockKey(key), key, second, TimeUnit.SECONDS);
}
}
二、实际使用
场景:假设现在我们有一个订单需要扣减库存,我们怎么保证同一时间段只有一个线程操作一个库存呢?
使用redisLock.tryLock来尝试获取锁
,获取成功后处理业务逻辑,业务不管成功失败都得在finally 释放锁
redisLock.tryLock方法是单线程
的所有不存在多线程加锁成功的现象,有效的防止并发
,
@Resource
private RedisTemplate<String, String> redisTemplate;
@Transactional(rollbackFor = Exception.class)
private boolean reduceStock(String stockId,Integer reduceQuanity) {
// 是否加锁成功
Boolean locked = false;
try {
// 加锁,设置锁超时时间,等待锁的时间
locked = redisLock.tryLock(stockId, 120,20);
if (!locked) {
// 给原来的锁追加时间
redisLock.reLock(stockId, 120);
throw new RuntimeException("当前库存有其他订单在操作");
}
// 库存是否足够
if(getStockQuantity(stockId)>reduceQuanity){
// 扣减库存
int count = reduceQuantity(stockId,reduceQuanity);
if(count==0){
throw new RuntimeException("库存扣减失败");
}
}
return true;
} finally {
// 业务处理成功后释放锁
if (locked) {
redisLock.unlock(stockId);
}
}
}
public Integer getStockQuantity(String stockId){
// select quantity from stock where id = #{stockId}
}
public int reduceQuantity(String stockId,Integer quantity){
// update stock set quantity = quantity - #{quantity} where id = #{stockId}
}
提示:需要注意的是所有的业务逻辑都应该写在try{}块中,finally 记得释放锁