新建RedisLock类。
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);// 取锁成功,直接返回falseif (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接口类
/*** 根据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实现类
@DubboService
public class RedisLockServiceImpl implements IRedisLockService {private static final Log LOG = LogFactory.get(RedisLockServiceImpl.class);@Overridepublic RedisLock getLock(String key) {if (StringUtils.isBlank(key)) {LOG.error("getLock: key is null");throw new BusinessException("取RedisLock的key不能为空");}return new RedisLock(key);}@Overridepublic RedisLock getLock(String key, Long expire) {if (expire == null) {LOG.debug("getLock: expire is null");return this.getLock(key);}return new RedisLock(key, expire);}@Overridepublic 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);}@Overridepublic 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);}@Overridepublic void unlock(RedisLock redisLock) {if (redisLock != null) {redisLock.unlock();}}
简单的运用
@Override@Transactionalpublic 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();}}}