Redis分布式锁
1.为什么要用redis锁
JVM本身有提供锁的机制,当然也只局限于当前的jvm中。如果是多个jvm进行操作,jvm提供的锁就会失效。所以我们需要一个多实例之间仍然可以唯一标识。正好redis支持,通过使用SETNX 命令
SETEX key seconds value
2. 手写一个简单的Redis分布式锁。
暂时没有考虑可重入性,即获取不到锁的线程是否应该被阻塞,执行代码异常等问题,
package cn.zl.springbootdemo.redis;
import cn.zl.springbootdemo.utils.SpringContextUtil;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.concurrent.TimeUnit;
public class RedisLock {
private RedisTemplate redisTemplate;
private String value;
private String key;
private int timeout = 30;
public RedisLock(String value, String key) {
this.value = value;
this.key = key;
}
public void lock() {
// 因为redis没有注入到容器中,所以通过工具类来获取
redisTemplate = (RedisTemplate) SpringContextUtil.getBean("redisTemplate");
// 获取锁逻辑
// 调用setnx,如果设置成功,结束方法,如果设置不成功,自选。
// 此命令是一个原子性操作
Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.SECONDS);
while (!aBoolean){
aBoolean = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.SECONDS);
}
return;
}
public void unlock(){
// 释放锁
redisTemplate.delete(key);
}
}
2.1 实现redis分布式锁,需要考虑的因素
唯一性: 保证锁的唯一性,即在所有的客户端上只能存在一份。可以通过SetNX命令 [SET if Not Exists]
可重入: 可重入性即被锁住的代码块可以被已经持有锁的线程再次执行。通过添加标识可以实现。
阻塞: 即没有获取到锁的时候,应该怎么处理,是自选?还是进行挂起。如果被挂起就涉及到何时被唤醒。
3. 使用redisson
此处是SpringBoot + redisson 结合来使用。
第一步引入依赖:
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.13.6</version>
</dependency>
配置:
@Configuration
public class SpringBootConfig {
/**
* 配置RedissonClient来进行使用RedisLock
*
* @return
*/
@Bean
public RedissonClient redissonClient(){
Config config = new Config();
config.useSingleServer()
.setAddress("redis://localhost:6379")
.setDatabase(0);
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
}
}
测试:
package cn.zl.springbootdemo.controller;
import cn.zl.springbootdemo.model.City;
import cn.zl.springbootdemo.redis.RedisLock;
import cn.zl.springbootdemo.service.CityService;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @ClassName : CityController
* @Description :
* @Author : zl
* @Date: 2021-07-17 18:06
*/
@RestController
public class CityController{
@Autowired
CityService cityService;
@Autowired
private RedissonClient redissonClient;
@RequestMapping("/city/lock1")
public void lock1() {
RLock lock = redissonClient.getLock("1111");
lock.lock(30, TimeUnit.SECONDS);
System.out.println("lock1获取到锁了");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("lock1释放了锁");
lock.unlock();
}
}
@RequestMapping("/city/lock2")
public void lock2(){
RLock lock = redissonClient.getLock("1111");
System.out.println("lock2尝试获取锁");
lock.lock(30,TimeUnit.SECONDS);
System.out.println("lock2获取到锁了");
lock.unlock();
System.out.println("lock2释放了锁");
}
}
关于原理有空再研究吧。