本质是利用redis的setnx()方法的特性来加锁,setnx()即key不存在则设置key,否则直接返回false,要求在分布式系统中使用同一个redis服务,以下提供两种解决方案:
1、直接使用redisTemplate:这其实并不能完全保证高并发下的安全问题,因为可能在锁过期之后该线程尚未执行完,这时其他线程是可以进入到同步代码中的,因此还需要开启一个守护线程来延续过期时间
public class SyncLock{
@Autowired
private StringRedisTemplate stringRedisTemplate;
public String syncStock() {
String lockKey = "lockKey";
String clientId = UUID.randomUUID().toString();
try {
//将设置key值和超时时间作为原子操作,低版本的redisTemplate不支持该方法
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 10, TimeUnit.SECONDS);
if (!result)
return "error";
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));//获取库存
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
} else {
return "库存不足!";
}
} finally {
if (clientId.equals(stringRedisTemplate.opsForValue().get(lockKey)))
stringRedisTemplate.delete(lockKey);//同时各个线程释放各自加的锁
}
return "成功!";
}
}
2、使用redisson:redisson会自动为我们开启守护线程延长锁的时间
public class SyncLock{
@Autowired
private Redisson redisson;
@Autowired
private StringRedisTemplate stringRedisTemplate;
public String syncStock() {
String lockKey = "lockKey";
RLock lock = redisson.getLock(lockKey);
try {
lock.tryLock();
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));//获取库存
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
} else {
return "库存不足!";
}
} finally {
lock.unlock();
}
return "成功!";
}
}
redisson的原理示意图: