新建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 redis lock.call(\" get\" ,KEYS[1]) == ARGV[1] " ) ; sb.append( "then " ) ; sb.append( " return redis lock.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 redis Template = SpringUtils.getBean( StringRedisTemplate.class) ; locked = redis Template.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 = redis Template.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 redis Template = SpringUtils.getBean( StringRedisTemplate.class) ; redis Template.delete( lockKey) ; }
redis _service_196">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 redis Lock 锁对象* @date 2020 年9月29日14:55:54*/void unlock( RedisLock redis Lock) ;
redis _service_241">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 redis Lock) { if ( redis Lock != null) { redis Lock.unlock( ) ; } }
简单的运用
@Override@Transactionalpublic void updateUser( V3User user) { //新增用户后立马更新用户 可能出现并发情况 导致数据重复String lockKey = "employeeResume::" + user.getId( ) ; RedisLock lock = redis LockService.getLock( lockKey, 10L, TimeUnit.SECONDS , 1 ) ; boolean locked = lock.tryLock( ) ; // 加锁失败,说明已经有别的程序在执行下面的保存操作,直接返回成功if ( ! locked) { return ; } try { 具体实现内容} finally { if ( lock != null && lock.isLocked( )) { lock.unlock( ) ; } } }