问题背景
Redisson做分布式锁是目前比较流行的方式,但是在使用的过程中遇到一些坑:
- Redisson的分布式锁只能通过创建锁的线程进行解锁,正所谓解铃还须系铃人,不是同一个线程解锁会报异常
- 因为Redisson是为锁而生,所以一开始设计的时候,为了防止死锁,默认锁的过期时间为30S
- 当时我居然傻到用单元测试来测试Redisson的分布式锁,我太傻了,单元测试之后马上就会结束项目运行,那么就没有线程持有锁了,更别说还需要同线程解锁了
注意事项:
- 代码是从我的这篇文章进行添加修改的
- 可以自己创建工程,也可以下载源码进行参考
- 默认已安装redis,可以使用安装包安装看这篇文章,使用docker安装看这篇文章
项目搭建
1 redisson的默认锁时间为30s
2 添加redisson工具类
package com.yg.redisson.utils;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class DistributedRedisLock {
@Autowired
private RedissonClient redissonClient;
/**
* 加锁
*
* @param lockName
* @return
*/
public Boolean lock(String lockName) {
if (redissonClient == null) {
log.info("DistributedRedisLock redissonClient is null");
return false;
}
try {
RLock lock = redissonClient.getLock(lockName);
// 默认是30S释放锁, 这里设置12小时
lock.lock(12 * 60 * 60L, TimeUnit.SECONDS);
log.info(" lock [{}] success", lockName);
return true;
} catch (Exception e) {
log.info("加锁失败", e);
log.error("DistributedRedisLock lock [{}] Exception:", lockName, e);
return false;
}
}
/**
* 加锁
*
* @param lockName
* @return
*/
public Boolean lockWithExpire(String lockName, long expire) {
if (redissonClient == null) {
log.info("DistributedRedisLock redissonClient is null");
return false;
}
try {
RLock lock = redissonClient.getLock(lockName);
// 锁expire秒后自动释放,防止死锁
lock.lock(expire, TimeUnit.SECONDS);
// 加锁成功
return true;
} catch (Exception e) {
log.info("加锁失败", e);
log.error("DistributedRedisLock lock [{}] Exception:", lockName, e);
return false;
}
}
/**
* 释放锁
*
* @param lockName
* @return
*/
public Boolean unlock(String lockName) {
if (redissonClient == null) {
log.info("DistributedRedisLock redissonClient is null");
return false;
}
try {
RLock lock = redissonClient.getLock(lockName);
log.info("lock:{}", lock.isLocked());
log.info("lock.isHeldByCurrentThread: {}", lock.isHeldByCurrentThread());
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
log.info(" unlock [{}] success", lockName);
return true;
}
return false;
} catch (Exception e) {
log.error("DistributedRedisLock unlock [{}] Exception:", lockName, e);
return false;
}
}
}
3 添加使用分布式锁的服务
package com.yg.redisson.service;
import com.yg.redisson.utils.DistributedRedisLock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
/**
* @Author suolong
* @Date 2022/5/7 10:04
* @Version 2.0
*/
@Slf4j
@Service
public class RedissonService {
@Autowired
DistributedRedisLock distributedRedisLock;
@PostConstruct
public void distributeLockTest(){
String lockName = "yuange";
//加锁
if(distributedRedisLock.lock(lockName)){
log.info("lock success");
//do something 锁住你要执行的程序
/*
*/
}else{
log.error("lock failed");
}
//解锁
if(distributedRedisLock.unlock(lockName)){
log.info("unlock success");
}else{
log.error("unlock failed");
}
}
}
4 项目目录
总结
- 如果被锁的逻辑会大于30s,需要自己设置过期时间
- 需要同个线程进行解锁
作为程序员第 124 篇文章,每次写一句歌词记录一下,看看人生有几首歌的时间,wahahaha ...