新建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) ;
// 取锁成功,直接返回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接口类
/**
* 根据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) ;
@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( ) ;
}
}
简单的运用
@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( ) ;
}
}
}