1 Redisson
直接上官方推荐Redisson
<!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.9.0</version>
</dependency>
配置
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private String port;
@Value("${spring.redis.password}")
private String password;
@Bean(destroyMethod = "shutdown")
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress("redis://" + host + ":" + port)
.setPassword(password);
return Redisson.create(config);
}
使用
@Autowired
private RedissonClient redissonClient;
public Boolean lock(Long userId) throws InterruptedException {
String lockKey = generateLockKey(userId);
RLock lock = redissonClient.getLock(lockKey);
return lock.tryLock(TIME_OUT_2_SECONDS, EXPIRE_20_SECONDS, TimeUnit.SECONDS);
}
2 自己实现
/**
* @author wujun
* @date 2018-10-31 15:37
*/
@Slf4j
@Component
public class UserAssetLock {
private static final String LOCK_KEY = "lock:asset:%d";
private static final Long EXPIRE_20_SECONDS_IN_MILLIS = 20 * 1000L;
private static final Long TIME_OUT_2_SECONDS_IN_NANOS = 2 * 1000 * 1000000L;
private static final Long SLEEP_TIME_200_MILLIS = 200L;
@Autowired
RedisTemplate<Object, Object> redisTemplate;
public Boolean lock(Long userId) {
Long startTime = System.nanoTime();
Boolean getLock = false;
while ((System.nanoTime() - startTime) < TIME_OUT_2_SECONDS_IN_NANOS) {
//当前时间未超出等待时间阈值则反复尝试获取锁
getLock = tryGetLock(userId);
if (getLock) {
break;
}
tryUnLockInvalidkey(userId);
trySleep200Millis();
}
return getLock;
}
/**
* 释放锁
* @param userId userId
*/
public void unLock(Long userId) {
String lockKey = generateLockKey(userId);
if (isLocked(userId)) {
redisTemplate.delete(lockKey);
}
}
/**
* 尝试获取锁
* @param userId userId
* @return 是否获取锁
*/
private Boolean tryGetLock(Long userId){
String lockKey = generateLockKey(userId);
Long expireMillis = System.currentTimeMillis() + EXPIRE_20_SECONDS_IN_MILLIS;
Boolean getLock = redisTemplate.opsForValue().setIfAbsent(lockKey, expireMillis);
if (getLock) {
System.out.println(System.currentTimeMillis());
redisTemplate.expire(lockKey, EXPIRE_20_SECONDS_IN_MILLIS, TimeUnit.MILLISECONDS);
}
return getLock;
}
/**
* 判断是否上锁
* @param userId userId
* @return 是否上锁
*/
private Boolean isLocked(Long userId) {
String lockKey = generateLockKey(userId);
return redisTemplate.hasKey(lockKey);
}
/**
* 尝试释放无效锁
* @param userId userId
*/
private void tryUnLockInvalidkey(Long userId) {
if (isInvalid(userId)) {
unLock(userId);
}
}
/**
* 判断是否为无效锁
* 锁存在,超过应失效时间却未失效,则为无效锁
*
* @param userId userId
* @return 是否为无效锁
*/
private Boolean isInvalid(Long userId) {
Boolean isInvalid = false;
if (isLocked(userId)) {
//确保key存在
Long shouldExpireMillis = getExpireMillis(userId);
if (null == shouldExpireMillis) {
//key存在但过期时间为null
isInvalid = true;
} else {
//当前时间大于应过期的时间,说明key失效了但还没删除,可能是设置key有效期失败导致
isInvalid = System.currentTimeMillis() > shouldExpireMillis;
}
}
return isInvalid;
}
/**
* 获取应过期时间毫秒值
* @param userId userId
* @return 应过期时间
*/
private Long getExpireMillis(Long userId) {
String lockKey = generateLockKey(userId);
return (Long) redisTemplate.opsForValue().get(lockKey);
}
/**
* 休眠200毫秒
*/
private void trySleep200Millis() {
try {
Thread.sleep(SLEEP_TIME_200_MILLIS);
} catch (InterruptedException e) {
log.error("获取分布式锁休眠被中断:", e);
}
}
/**
* 生成key
* @param userId userId
* @return key
*/
private String generateLockKey(Long userId) {
return String.format(LOCK_KEY, userId);
}
}