1、简介
分布式锁有很多种实现方式,使用数据库、Redis、Zookeeper等都可以,但综合性能等使用最广泛的还是Redis分布式锁。
2、环境部分请参考
2.1 (一)Redis学习笔记 — redis在linux下的安装及基本配置
2.2 (二)Redis学习笔记 — redis集群配置
2.3 (三)Redis学习笔记 — springboot集成redis
3、分布式锁
3.1 普通分布式锁
@Autowired
private RedisTemplate redisTemplate;
/**
* 普通锁
* @param lockName 锁名称
* @return
*/
public boolean lock(String lockName) {
Boolean success = null;
try {
success = redisTemplate.opsForValue().setIfAbsent(lockName, "lock");
} catch (Exception e) {
e.printStackTrace();
}
return success != null && success;
}
/**
* 普通锁解锁
* @param lockName 锁名称
*/
public boolean unlock(String lockName) {
Boolean success = null;
try {
if (lockName != null && redisTemplate.hasKey(lockName)
&& "lock".equals(redisTemplate.opsForValue().get(lockName))) {
success = redisTemplate.delete(lockName);
}
} catch (Exception e) {
e.printStackTrace();
}
return success != null && success;
}
3.2 限时锁
@Autowired
private RedisTemplate redisTemplate;
/**
* 限时锁
* @param lockName 锁名称
* @param expireTime 期望时间(单位:秒),一般比最大业务预计完成时间稍长
* @return
*/
public boolean expireTimeLock(String lockName, long expireTime) {
Boolean success = null;
try {
success = redisTemplate.opsForValue().setIfAbsent(lockName, "expireTimeLock", expireTime, TimeUnit.SECONDS);
} catch (Exception e) {
e.printStackTrace();
}
return success != null && success;
}
/**
* 限时锁解锁,该锁解锁失败也无所谓,过期会自动解除
* @param lockName 锁名称
*/
public void unExpireTimeLock(String lockName) {
try {
if (redisTemplate.hasKey(lockName) && "expireTimeLock".equals(redisTemplate.opsForValue().get(lockName))) {
redisTemplate.delete(lockName);
}
} catch (Exception e) {
e.printStackTrace();
}
}
3.3 阻塞锁
@Autowired
private RedisTemplate redisTemplate;
/**
* 普通锁,如果加锁失败,可以重新尝试retryTimes次,所以最大加锁次数是retryTimes + 1次。
* 该加锁是阻塞加锁,解锁方式用普通锁解锁!
* @param lockName 锁名称
* @param retryTimes 重新尝试次数
* @return
*/
public boolean tryLock(String lockName, int retryTimes) {
boolean lock = lock(lockName);
if (retryTimes == 0) {
retryTimes = 3;
}
// 如果加锁失败,每过5s尝试加锁一次
int times = 1;
while (!lock && times <= retryTimes) {
//上面已经尝试加锁一次,所以如果要retry,先睡5s
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock = lock(lockName);
times ++;
}
return lock;
}
/**
* 限时锁,如果加锁失败,可以重新尝试retryTimes次,所以最大加锁次数是retryTimes + 1次。
* 该加锁是阻塞加锁,解锁方式用限时锁解锁!
* @param lockName 锁名称
* @param expireTime 期望时间(单位:秒),一般比最大业务预计完成时间稍长
* @param retryTimes 重新尝试次数
* @return
*/
public boolean tryExpireTimeLock(String lockName, long expireTime, int retryTimes) {
boolean expireTimeLock = expireTimeLock(lockName, expireTime);
if (retryTimes == 0) {
retryTimes = 3;
}
// 如果加锁失败,每过5s尝试加锁一次
int times = 1;
while (!expireTimeLock && times <= retryTimes) {
//上面已经尝试加锁一次,所以如果要retry,先睡5s
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
expireTimeLock = expireTimeLock(lockName, expireTime);
times ++;
}
return expireTimeLock;
}
3.4 线程锁
@Autowired
private RedisTemplate redisTemplate;
/**
* 线程锁,只有当前线程才能解锁
* 留一个小bug,线程锁名称应该和线程ID相关,这样才不会被别人误操作解除。
* @param lockName 锁名称
* @param currentThread 当前线程
* @return
*/
public boolean threadLock(String lockName, Thread currentThread) {
Boolean success = null;
try {
success = redisTemplate.opsForValue().setIfAbsent(lockName, String.format("threadLock_%s", String.valueOf(currentThread.getId())));
} catch (Exception e) {
e.printStackTrace();
}
return success != null && success;
}
/**
* 线程锁解锁
* @param lockName 锁名称
* @param currentThread 当前线程
* @return
*/
public boolean unThreadLock(String lockName, Thread currentThread) {
Boolean success = null;
try {
if (redisTemplate.hasKey(lockName) &&
String.format("threadLock_%s", String.valueOf(currentThread.getId())).equals(redisTemplate.opsForValue().get(lockName))) {
success = redisTemplate.delete(lockName);
}
} catch (Exception e) {
e.printStackTrace();
}
return success != null && success;
}
4、总结
以上是个人学习时博客到的所有内容的总结,其实个人觉得分布式锁应该还有自动续期的功能,那样要加一个守护线程,时刻去监测当前锁时间是否已经过了三分之二,如果过了自动将锁重置。这个只是对限时锁或者阻塞限时锁有用,且实现稍微麻烦,在这里不实现了。