新建RedisLock类。
bash
复制代码
public class RedisLock implements Serializable {
private static final long serialVersionUID = 3077854413624876404L;
private static final Log LOG = LogFactory.get(RedisLock.class);
/**
* 锁标志对应的key
*/
private String lockKey;
/**
* 将key 的值设为value ,当且仅当key 不存在,等效于 SETNX。
*/
private static final String NX = "NX";
/**
* seconds --- 以秒为单位设置 key 的过期时间,等效于EXPIRE key seconds
*/
private static final String EX = "EX";
/**
* 调用set后的返回值
*/
private static final String OK = "OK";
/**
* 取锁等待中线程等待时间
*/
private static final Long WAITING_SECONT = 200L;
/**
* 默认请求锁的等等时间,分钟
*/
private static final Integer WAIT_MINUTE = 1;
/**
* 默认锁的有效时间
*/
private static final Long EXPIRE = 5L;
/**
* 设置默认上锁时间单位
*/
private static final TimeUnit TIME_UNIT = TimeUnit.MINUTES;
/**
* 解锁的lua脚本
*/
private static final String UNLOCK_LUA;
/**
* 锁标记
*/
private volatile boolean locked = false;
static {
StringBuilder sb = new StringBuilder();
sb.append("if redislock.call(\"get\",KEYS[1]) == ARGV[1] ");
sb.append("then ");
sb.append(" return redislock.call(\"del\",KEYS[1]) ");
sb.append("else ");
sb.append(" return 0 ");
sb.append("end ");
UNLOCK_LUA = sb.toString();
}
/**
* 锁的有效时间(s)
*/
private Long expire = EXPIRE;
/**
* 请求锁的超时时间(ms)
*/
private Integer waitMinute = WAIT_MINUTE;
private TimeUnit timeUnit = TIME_UNIT;
/**
* Instantiates a new Redis lock.
*
* @param lockKey the lock key
*/
public RedisLock(String lockKey) {
this.lockKey = lockKey;
}
/**
* Instantiates a new Redis lock.
*
* @param lockKey the lock key
* @param expire the expire
*/
public RedisLock(String lockKey, Long expire) {
this.lockKey = lockKey;
this.expire = expire;
}
/**
* Instantiates a new Redis lock.
*
* @param lockKey the lock key
* @param expire the expire
* @param timeUnit the time unit
*/
public RedisLock(String lockKey, Long expire, TimeUnit timeUnit) {
this.lockKey = lockKey;
this.expire = expire;
this.timeUnit = timeUnit;
}
/**
* Instantiates a new Redis lock.
*
* @param lockKey the lock key
* @param expire the expire
* @param timeUnit the time unit
* @param waitMinute the wait minute
*/
public RedisLock(String lockKey, Long expire, TimeUnit timeUnit, Integer waitMinute) {
this.lockKey = lockKey;
this.expire = expire;
this.timeUnit = timeUnit;
this.waitMinute = waitMinute;
}
/**
* Try lock boolean.
*
* @return the boolean
*/
public boolean tryLock(){
try {
if (StringUtils.isBlank(lockKey)) {
LOG.debug("tryLock:key is blank");
return false;
}
String value = UUID.fastUUID().toString();
StringRedisTemplate redisTemplate = SpringUtils.getBean(StringRedisTemplate.class);
locked = redisTemplate.opsForValue().setIfAbsent(lockKey, value, expire, timeUnit);
// 取锁成功,直接返回false
if (locked) {
LOG.debug("tryLock:取锁成功");
return locked;
} else {
if (waitMinute == null) {
LOG.debug("tryLock: 等待加锁时间为null,则不再尝试去加锁,直接返回false");
return false;
}
if (waitMinute < 0) {
LOG.debug("tryLock: 等待加锁时间小于0,则不再尝试去加锁,直接返回false");
return false;
}
long loseTime = DateUtil.offsetMinute(new Date(), waitMinute).getTime();
// 不断尝试获取锁成功返回
while (System.currentTimeMillis() < loseTime) {
locked = redisTemplate.opsForValue().setIfAbsent(lockKey, value, expire, timeUnit);
// 加锁成功
if (locked) {
return locked;
}
try {
Thread.sleep(WAITING_SECONT);
} catch (InterruptedException e) {
LOG.error(e, "tryLock 等待加锁的时候异常");
}
}
return locked;
}
} catch (Exception e) {
LOG.error(e, "tryLock error");
return false;
}
}
/**
* Is locked boolean.
*
* @return the boolean
*/
public boolean isLocked(){
return locked;
}
/**
* Unlock.
*/
public void unlock(){
StringRedisTemplate redisTemplate = SpringUtils.getBean(StringRedisTemplate.class);
redisTemplate.delete(lockKey);
}
redis锁 service接口类
bash
复制代码
/**
* 根据key取锁
* @param key 健值
* @return RedisLock
*/
RedisLock getLock(String key);
/**
* 根据key取锁
* @param key 健值
* @param expire 过期时间
* @return RedisLock
*/
RedisLock getLock(String key, Long expire);
/**
* 根据key取锁
* @param key 健值
* @param expire 过期时间
* @param timeUnit 过期时间单位
* @return RedisLock
*/
RedisLock getLock(String key, Long expire, TimeUnit timeUnit);
/**
* 根据key取锁,可以设置等待时间和过期时间
* @param key key
* @param waitMinute 等待时间,单位是分钟,在这个时间内会多次尝试获取锁,超过这个时间还没获得锁,就返回false
* @param expire 加锁过期时间,为null时候,默认为-1L
* @param timeUnit 单位,为null默认为Minutes
* @return boolean true 成功,false 失败
*/
RedisLock getLock(String key, Long expire, TimeUnit timeUnit, Integer waitMinute);
/**
* 释放锁
* @param redisLock 锁对象
* @date 2020年9月29日14:55:54
*/
void unlock(RedisLock redisLock);
redis锁 service实现类
bash
复制代码
@DubboService
public class RedisLockServiceImpl implements IRedisLockService {
private static final Log LOG = LogFactory.get(RedisLockServiceImpl.class);
@Override
public RedisLock getLock(String key) {
if (StringUtils.isBlank(key)) {
LOG.error("getLock: key is null");
throw new BusinessException("取RedisLock的key不能为空");
}
return new RedisLock(key);
}
@Override
public RedisLock getLock(String key, Long expire) {
if (expire == null) {
LOG.debug("getLock: expire is null");
return this.getLock(key);
}
return new RedisLock(key, expire);
}
@Override
public RedisLock getLock(String key, Long expire, TimeUnit timeUnit) {
if (timeUnit == null) {
LOG.debug("getLock: timeUnit is null");
return this.getLock(key, expire);
}
return new RedisLock(key, expire, timeUnit);
}
@Override
public RedisLock getLock(String key, Long expire, TimeUnit timeUnit, Integer waitMinute) {
if (waitMinute == null) {
LOG.debug("getLock: waitMinute is null");
return this.getLock(key, expire, timeUnit);
}
return new RedisLock(key, expire, timeUnit, waitMinute);
}
@Override
public void unlock(RedisLock redisLock) {
if (redisLock != null) {
redisLock.unlock();
}
}
简单的运用
bash
复制代码
@Override
@Transactional
public void updateUser(V3User user) {
//新增用户后立马更新用户 可能出现并发情况 导致数据重复
String lockKey = "employeeResume::" + user.getId();
RedisLock lock = redisLockService.getLock(lockKey, 10L, TimeUnit.SECONDS, 1);
boolean locked = lock.tryLock();
// 加锁失败,说明已经有别的程序在执行下面的保存操作,直接返回成功
if (!locked) {
return;
}
try {
具体实现内容
} finally {
if (lock != null && lock.isLocked()) {
lock.unlock();
}
}
}