1、使用条件:依赖
<dependency>
<groupId>monkey.do</groupId>
<artifactId>data-bus-redis-client</artifactId>
</dependency>
本文分布式锁,基于Redis的方式去实现分布式锁,具体实现用到的是 Redisson。通俗来讲,就是在Redis基础上实现的分布式工具集合。 分布式锁的使用分成以下 3 步: 1. 获取锁:根据唯一的 key 去 redis 获取锁。 2. 加锁:拿到锁后在指定的等待时间内不断尝试对其加锁,超过等待时间则加锁失败。 3. 解锁:分成两种情形: • 第一如果在加锁的时候指定了自动释放时间,那么在此时间范围内业务提前完成的话就在 finally 手动释放锁,而如果业务没有完成也会自动释放锁,所以指定自动释放时间需要做非常仔细的考量; • 第二就是没有指定自动释放时间,由于 redisson 有 watch dog (看门狗)机制,watch dog 默认的 releaseTime 是 30s,给锁加上 30s 的自动释放时间,并且每隔 releaseTime / 3 即 10 s 去检查业务是否完成,如果没有完成重置 releaseTime 为 30 s, 即锁的续约,所以一个业务严重阻塞的话会造成系统资源的极大浪费。
2、使用示例:一般情况,没有异步方法的情况下
@Autowired
private RedissonClient redissonClient;
public void test(Long id) throws InterruptedException {
//获取锁
RLock lock = redissonClient.getLock("my-lock-name-" + id);
//加锁,参数1:获取锁的最大等待时间(期间会重试),参数2:锁自动释放时间,参数3:时间单位
//注意:如果指定锁自动释放时间,不管业务有没有执行完,锁都不会自动延期,即没有 watch dog 机制。
boolean isLock = lock.tryLock(1, 2, TimeUnit.SECONDS);
try {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
if (isLock) {
System.out.println(format.format(System.currentTimeMillis()) + "获取分布式锁成功");
dowork();//模拟业务处理
System.out.println(id);
System.out.println(format.format(System.currentTimeMillis()) + "业务完成");
} else {
System.out.println(format.format(System.currentTimeMillis()) + "获取分布式锁失败");
}
} catch (Exception e) {
throw new RuntimeException("业务异常");
} finally {
//当前线程未解锁
if (lock.isHeldByCurrentThread() && lock.isLocked()) {
//释放锁
System.out.println("解锁");
lock.unlock();
}
}
}
private void doWork() {
// 模拟业务逻辑处理
try {
Thread.sleep(5000); // 让线程持有锁5秒
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
3、使用过程中遇到的问题:
@Async当把Rlock对象传入异步方法的时候会无法解锁,才发现redis分布式锁只能解锁同一个线程加的锁,在项目中因业务逻辑限制,需要在异步方法外部创建锁对象,把锁对象传入异步方法内部后无法解锁,这个时候需要用异步解锁的方法:
先创建redis分布式锁对象,然后在异步方法外部获取线程ID,把线程ID传入异步方法,然后通过异步解锁方法解锁
//获取线程ID
long threadId = Thread.currentThread().getId();
//传参传进异步方法
if(lock.isLocked() && lock.isHeldByThread(threadId)){
// 异步解锁--指定线程id
lock.unlockAsync(threadId);
}
①第一步
public Result reCommit(long userId, @RequestBody ReserveNoticeRequest request) throws InterruptedException {
RLock redislock = redissonClient.getLock(read_record_batch_key+ resource_id_read);
// //加锁,参数1:获取锁的最大等待时间(期间会重试),参数2:锁自动释放时间,参数3:时间单位
// //注意:如果指定锁自动释放时间,不管业务有没有执行完,锁都不会自动延期,即没有 watch dog 机制。
boolean isLock = redislock.tryLock(1, 60*60, TimeUnit.SECONDS);
if (!isLock) {
return new Result<>(901, "已被锁定,不能导入!");
}
KoiCsvResponse csvResponse;
try (InputStreamReader inputStreamReader = new InputStreamReader(reserveNoticeUtil.execute(request.getFileId()).getData(), "GBK")) {
Result<KoiCsvResponse> result = readLogService.parseCsvData(inputStreamReader);
if (!result.isSuccess()) {
//当前线程未解锁
if (redislock.isHeldByCurrentThread() && redislock.isLocked()) {
//释放锁
redislock.unlock();
}
log.info("【预约导入】抄表录入文件流解析失败: " + ObjectMapper.toJson(result));
return result;
}
csvResponse = result.getData();
if (CollectionUtils.isEmpty(csvResponse.getDataList())) {
//当前线程未解锁
if (redislock.isHeldByCurrentThread() && redislock.isLocked()) {
//释放锁
redislock.unlock();
}
return new Result<>(902, "无上传数据!");
}
} catch (Exception e) {
//当前线程未解锁
if (redislock.isHeldByCurrentThread() && redislock.isLocked()) {
//释放锁
redislock.unlock();
}
return new Result<>(903, "导入失败!");
}
//获取线程ID
long threadId = Thread.currentThread().getId();
if (csvResponse.getType() == 1) {
readLogService.createBatchCsv(userId,csvResponse.getDataList(), financeMonth,request,read_record_batch_key, resource_id_read,redislock,threadId);
} else if (csvResponse.getType() == 2) {
readLogService.reviewBatchCsv(userId, csvResponse.getDataList(), financeMonth, request, read_record_batch_key, resource_id_read,redislock,threadId);
}
return Result.success();
}
②第二步
@Async
public void createBatchCsv(Long memberId, List<KoiReadRecordCsvResponse> responses,
String accountingMonth, ReserveNoticeRequest request,
String batchKey, String resourceId, RLock remoteResourceLock,long threadId) {
try {
Long taskId = request.getId();
//代码逻辑
}catch (Exception e) {
log.info("抄表导入异常: " + JSONObject.toJSONString(e));
throw new RuntimeException();
} finally {
if (remoteResourceLock.isLocked()&&remoteResourceLock.isHeldByThread(threadId)){
remoteResourceLock.unlockAsync(threadId);
}
}
}
1459

被折叠的 条评论
为什么被折叠?



