自己的学习笔记,如有错误,请您指导!
1.1直接设置锁(若在释放锁之前抛异常 则这个锁就不会被释放)
public void testLock(){
ValueOperations valueOperations = redisTemplate.opsForValue();
//先判断 如果key不存在 才设置成功 相当于上锁
Boolean isLock = valueOperations.setIfAbsent("k1", "v1");
if (isLock){
valueOperations.set("name","xxxx");
String name = (String) valueOperations.get("name");
System.out.println("name = " + name);
//模拟异常
int i = 10/0;
//操作结束 删除锁
//但是若在释放锁之前抛异常 则这个锁就不会被释放 第二个线程 进来还是获取不到锁
redisTemplate.delete("k1");
}else {
System.out.println("有线程在使用,请稍后再试");
}
}
1.2设置有时效的锁(抛异常之后锁也会释放)
public void testLock1(){
ValueOperations valueOperations = redisTemplate.opsForValue();
Boolean isLock = valueOperations.setIfAbsent("k1", "v1", 5, TimeUnit.SECONDS);
if (isLock){
valueOperations.set("name","xxxx");
String name = (String) valueOperations.get("name");
System.out.println("name = " + name);
//模拟异常
int i = 10/0;
//操作结束 删除锁
//因为抛异常了 所以不会走到这一步,但是前面上锁的时候设置了时间,5秒之后 就可以获取该锁了
redisTemplate.delete("k1");
}
}
1.3设置有时效的锁 (出现的问题)
public void testLock1(){
ValueOperations valueOperations = redisTemplate.opsForValue();
Boolean isLock = valueOperations.setIfAbsent("k1", "v1", 5, TimeUnit.SECONDS);
if (isLock){
//模拟做自己的业务耗时7秒
try{
TimeUnit.SECONDS.sleep(7);
} catch (InterruptedException e) {
e.printStackTrace();
}
redisTemplate.delete("k1");
}
}
但是会有问题 ,假设A线程获取到锁,但是它执行耗时了7秒,所以他还没执行完的时候就已经所就已经被释放了(然后B线程获取该锁),那么当他执行完以后执行redisTemplate.delete(“k1”) 释放的是B线程的锁。假设线程多了以后并且执行业务时间也更长 ,那么就会更乱。
1.4使用lua脚本可以原子性的执行多条redis指令
为了解决上面的问题,可以在删除锁的时候判断这个锁是不是自己的锁,所以在设置锁定时候使用UUID生成一个随机的串当作自己的锁的value。
但是在释放锁的过程中需要先获取锁,然后判断是不是自己的锁,最后释放锁,我们需要原子性的执行这三条操作,所以使用lua脚本。
1.在resource下创建lock.lua
if redis.call("get",KEYS[1])==ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
脚本解释:redis get到KEYS集合(后面调用传进来的)里key的value 然后和ARGV集合(后面调用传进来的)里的value比对。如果一样就执行redis.call(“del”,KEYS[1]),也就是执行del 对KEYS集合里的key删除。
2.在RedisConfig中配置(配置类 注意加上@Configuration)
@Configuration
public class RedisConfig {
@Bean
public DefaultRedisScript<Boolean> script(){
DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
//lock.lua 脚本位置和application.yml同级目录
redisScript.setLocation(new ClassPathResource("lock.lua"));
redisScript.setResultType(Boolean.class);
return redisScript;
}
3.使用lua脚本的锁
public void testLock2(){
ValueOperations valueOperations = redisTemplate.opsForValue();
//给k1设置随机值作为唯一标识
String value = UUID.randomUUID().toString();
Boolean isLock = valueOperations.setIfAbsent("k1", value,5,TimeUnit.SECONDS);
if (isLock){
valueOperations.set("name","xxxx");
String name = (String) valueOperations.get("name");
System.out.println("name = " + name);
System.out.println(valueOperations.get("k1"));
//在脚本中进行比对 Collections.singletonList("k1")就是KEYS[1] value就是ARGV[1]
Boolean result = (Boolean) redisTemplate.execute(script, Collections.singletonList("k1"), value);
System.out.println(result);
}else {
System.out.println("有线程在使用,请稍后再试");
}
}
4.使用redis的事务
public void testLock2(){
ValueOperations valueOperations = redisTemplate.opsForValue();
//给k1设置随机值作为唯一标识
String value = UUID.randomUUID().toString();
Boolean isLock = valueOperations.setIfAbsent("k1", value,5,TimeUnit.SECONDS);
if (isLock){
valueOperations.set("name","xxxx");
String name = (String) valueOperations.get("name");
System.out.println("name = " + name);
System.out.println(valueOperations.get("k1"));
while (true){
redisTemplate.watch("k1");
if (valueOperations.get("k1").equals(value)){
redisTemplate.setEnableTransactionSupport(true);
redisTemplate.multi();
redisTemplate.delete("k1");
List list = redisTemplate.exec();
if (list == null){//说明在监视期间被动过
continue;
}
}
redisTemplate.unwatch();
break;
}
}else {
System.out.println("有线程在使用,请稍后再试");
}
}