在【Redis Jedis实战(含Spring Boot)】基础上做如下修改:
RedisDistributedLockApplication.java
package com.java.ashare.redis.distributedlock;
import java.util.concurrent.TimeUnit;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.java.ashare.redis.utils.RedisUtil;
@Component
public class RedisDistributedLockApplication {
/**
* 普通分布式锁
*
* 考虑异常情况:
* 1、RedisUtil.del("lockKey")之前抛出异常导致加锁key没有删除掉,后面线程无法获得锁,通过finally解决;
* 2、如果加锁成功后不是抛出异常而是宕机,那么加锁key也无法删除,后面线程无法获得锁,通过设置key过期时间解决;
* 3、业务执行时间大于key过期时间,那么第一个线程RedisUtil.del("lockKey")操作会把第二个线程加的锁key删除掉,那么第三个线程加锁成功,最终导致锁失效,线程不安全,出现超卖情况,
* 解决方案:加锁成功后开启一个子线程执行java timer定时任务,定时重新设置过期时间,间隔时间不能超过key过期时间;
*/
public void decrStock() {
try {
// 加锁
// spring-data-redis-2.1以上版本才支持setnx+expire原子操作
Boolean result = RedisUtil.valueOperations().setIfAbsent("lockKey", "1", 10, TimeUnit.SECONDS);
if(!result) {
System.out.println("加锁失败!");
} else {
System.out.println("加锁成功!");
int stock = Integer.parseInt(RedisUtil.valueOperations().get("stock"));
if(stock > 0) {
int realStock = stock - 1;
RedisUtil.valueOperations().set("stock", realStock + "");
System.out.println("扣减成功,剩余库存:" + realStock);
} else {
System.out.println("扣减失败,库存不足!");
}
}
} finally {
// 释放锁
RedisUtil.del("lockKey");
}
}
}
以上分布式锁实现,我们需要考虑很多东西,稍微不注意就可能在高并发环境下出现各种异常情况,怎么办呢?
有,使用Redisson实现分布式锁,封装以上逻辑,代码更加简单。
参考:【7-1、Redis Redisson分布式锁实战(含Spring Boot)】