说明: 为了脚手架的的实现,此处不使用Redission来直接实现,完全手动实现一个抢占式的分布式锁
使用场景:
1)任务调度(集群环境下,一个服务的多个实例的任务不想同一时间都进行执行)
2)并发修改相关(操作同一个数据)
1. 添加字符串校验依赖
<!--字符串校验-->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
2. 自定义异常--运行时异常类ShareLockException
package com.ssm.redis.exception;
public class ShareLockException extends RuntimeException {
//自定义异常
public ShareLockException(String message) {
super(message);
}
}
3. 实现: Redis分布式抢占锁RedisShareLockUtil
package com.ssm.redis.util;
import com.ssm.redis.exception.ShareLockException;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class RedisShareRedLock {
@Autowired
private RedisUtil redisUtil;
private Long OUT_TIME = 1000L; //10s
/**
* @param lockKey 锁的key
* @param requestId 锁的标识,类似线程的threadID, 删锁时判断是否为自己的锁
* 防止误删(业务1未执行完,锁自动删除,业务二获取锁,此时业务1执行完,删锁时把业务2的锁删了)
* @param time
* @return
*/
public boolean lock(String lockKey, String requestId, Long time) {
//1.参数校验
if(StringUtils.isBlank(lockKey) || StringUtils.isBlank(requestId) || time <= 0) {
//commons.lang包中的isBlank会判断null和空字符串两种情况
throw new ShareLockException("分布式锁-加锁参数异常"); // 自定义异常
}
//2.加锁可自旋(一定时间内可重复获取锁)
long currentTime = System.currentTimeMillis(); //获取当前毫秒数
long outTime = currentTime + OUT_TIME;
Boolean result = false;
while(currentTime < outTime) {
//3.利用redis的setnx设置锁
result = redisUtil.setNx(lockKey, requestId, time, TimeUnit.MILLISECONDS);
if(result) { //为true获取锁成功
return result;
}
//获取失败,等待100ms重新尝试
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
currentTime = System.currentTimeMillis();
}
return result;
}
public boolean unLock(String lockKey, String requestId) {
if(StringUtils.isBlank(lockKey) || StringUtils.isBlank(requestId)) {
throw new ShareLockException("分布式锁-解锁参数异常");
}
try {
//获取当前锁的值
String value = redisUtil.get(lockKey);
if(requestId.equals(value)) {
//是自己的锁,删除
boolean del = redisUtil.del(lockKey);
return true;
}
} catch (Exception e) {
//补日志
}
return false;
}
public boolean tryLock(String lockKey, String requestId, Long time) {
//只是尝试获取锁,没有自旋操作
if(StringUtils.isBlank(lockKey) || StringUtils.isBlank(requestId) || time <= 0) {
throw new ShareLockException("分布式锁-加锁参数异常");
}
return redisUtil.setNx(lockKey, requestId, time, TimeUnit.MILLISECONDS);
}
}