场景:由于我们是使用springcloud中的分布式场景,在高并发的情况下,会对其进行加锁操作,以下就是使用setNx来对其进行加锁操作,
pom文件依赖:
<!--spring 测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- 开启web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--springboot整合redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
下面是对配置文件中的内容:
server:
port: 8084
spring:
redis:
host: localhost
port: 6379 #redis中的端口号
具体的代码实现流程:全局锁类:分布式锁类的设计
/**
* @Author: Jason
* @Create: 2020/10/11 16:37
* @Description 全局锁类 主要有名称与value值
*/
@Data
public class Lock {
private String name;
private String value;
public Lock(String name, String value){
this.name = name;
this.value = value;
}
}
分布式锁类的设计:
@Component
public class DistributedLockHandler {
private static final Logger logger = LoggerFactory.getLogger(DistributedLockHandler.class);
private final static long LOCK_EXPIRE = 30 * 1000L; //单个业务持有锁的时间
private final static long LOCK_TRY_INTERVAL = 30L; //默认30ms一次
private final static long LOCK_TRY_TIMEOUT = 20 * 1000L; //默认尝试20s
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 尝试获取全局锁
* @param lock 锁的名称
* @return true 获取成功,false获取失败
*/
public boolean tryLock(Lock lock){
return getLock(lock, LOCK_TRY_TIMEOUT, LOCK_TRY_INTERVAL, LOCK_EXPIRE);
}
/**
*尝试获取全局锁
* @param lock 锁的名称
* @param timeout 获取锁的超时时间,单位ms
* @return true 获取成功, false 获取失败
*/
public boolean tryLock(Lock lock, long timeout){
return getLock(lock, timeout, LOCK_TRY_INTERVAL, LOCK_EXPIRE);
}
/**
* 尝试获取全局锁
* @param lock 锁的名称
* @param timeout 获取锁的超时时间
* @param tryInterval 获取成功 true 代表成功 false 代表失败
* @return
*/
public boolean tryLock(Lock lock, long timeout, long tryInterval){
return getLock(lock, timeout, tryInterval, LOCK_EXPIRE);
}
/**
* 尝试获取全局锁
* @param lock 锁的名称
* @param timeout 获取锁的超时时间
* @param tryInterval 尝试间隔次数
* @param lockExpireTime 锁的过期时间
* @return
*/
public boolean tryLock(Lock lock, long timeout, long tryInterval, long lockExpireTime){
return getLock(lock, timeout, tryInterval, LOCK_EXPIRE);
}
/**
* 操作redis获取全局锁
* @param lock 锁的名称
* @param timeout 获取的超时时间
* @param tryInterval 多少ms尝试一次
* @param lockExpireTime 获取成功后锁的过期时间
* @return true 获取成功 false 获取失败
* @throws InterruptedException
* 其原理是:在判断是否有key的基础上,进行循环,只有当不存在key才对其进行加锁操作,其他不对其进行任何操作
*/
public boolean getLock(Lock lock, long timeout, long tryInterval, long lockExpireTime) {
if(StringUtils.isEmpty(lock.getName()) || StringUtils.isEmpty(lock.getValue())){
return false;
}
long startTime = System.currentTimeMillis();
do{
if (!stringRedisTemplate.hasKey(lock.getName())){
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
ops.set(lock.getName(),lock.getValue(), lockExpireTime);
return true;
}else{//存在锁
logger.debug("lock is exist");
}
//尝试超过设定值之后直接跳出循环
if (System.currentTimeMillis() - startTime > timeout){
return false;
}
try {
Thread.sleep(tryInterval);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
while (stringRedisTemplate.hasKey(lock.getName()));
return false;
}
/**
* 释放锁方法
* @param lock
*/
public void releaseLock(Lock lock){
if (!StringUtils.isEmpty(lock.getName())){
stringRedisTemplate.delete(lock.getName());
}
}
但是这种方法redis不推荐,因为这是一个耗时的操作影响性能
redis推荐使用redission锁,具体的代码流程:
首先是定义好一个获取锁之后的接口 - 然后就是定义好分布式锁的接口
pom文件是对前面进行改造之后的只是添加一个pom文件:
<!--redisson分布式锁-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.7.0</version>
</dependency>```
代码分析:这种感觉更像一种设计模式
/**
* @Author: Jason
* @Create: 2020/10/12 20:13
* @Description 获取锁之后的操作
*/
public interface AquiredLockWorker<T> {
T invokeAfterLockAquire() throws Exception;
}
/**
* @Author: Jason
* @Create: 2020/10/12 20:14
* @Description 分布式锁的管理类
*/
public interface DistributedLocker {
/**
* 获取锁
* @param resourceName 锁的名称
* @param worker 获取锁后的处理类
* @param <T>
* @return 处理完具体的业务逻辑要返回的数据
* @throws Exception
*/
<T> T lock(String resourceName, AquiredLockWorker<T> worker) throws Exception;
<T> T lock(String resourceName, AquiredLockWorker<T> worker , int lockTime) throws Exception;
}
@Component
public class RedisLock implements DistributedLocker {
private final static String LOCKER_PREFIX = "lock";
@Autowired
RedissonConnector redisConnection;
@Override
public <T> T lock(String resourceName, AquiredLockWorker<T> worker) throws Exception {
return lock(resourceName, worker, 100);
}
/**
*感觉更像一种设计模式一样
* @param resourceName
* @param worker
* @param lockTime
* @param <T>
* @return
* @throws Exception
*/
@Override
public <T> T lock(String resourceName, AquiredLockWorker<T> worker, int lockTime) throws Exception {
RedissonClient redisson = redisConnection.getRedissonClient();
RLock lock = redisson.getLock(LOCKER_PREFIX + resourceName);
//等待100秒之后,自动释放锁
boolean success = lock.tryLock(100, lockTime, TimeUnit.SECONDS);
if(success){
try{
return worker.invokeAfterLockAquire();
}finally {
lock.unlock();
}
}
throw new UnableToAquireLockException();
}
}
/**
* @Author: Jason
* @Create: 2020/10/12 20:18
* @Description 异常类
*/
public class UnableToAquireLockException extends RuntimeException{
public UnableToAquireLockException(){};
public UnableToAquireLockException(String message){
super(message);
}
public UnableToAquireLockException(String message, Throwable cause){
super(message,cause);
}
}