目录
问:当我们上锁setnx key 之后,服务器突然挂了怎么办?
在单体应用中,上锁可以,但是当你是分布式集群中,你其他服务器就识别不到这把锁了(因为JVM不能跨服务器访问),所以我们需要一个叫分布式锁的东西——>让所有服务器都能共享这把锁;
需要释放锁资源,别人才能拿到这个资源;
指令:
setnx key value:获取锁资源,赋值
del key:删除锁资源
个人感觉很像synchronized
我们可以设置过期时间
指令:expire key times:给这个key设置过期时间times
ttl key:查看还有多久过期(-1代表过期)
我们可以上锁的时候把过期时间一起设置了
命令:set key value nx ex times
set users 20 nx ex 10:意思就是nx代表上锁了,ex代表过期时间设置
@GetMapping("testLock")
public void testLock(){
String uuid = UUID.randomUUID().toString();
//1获取锁,setne
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid,3, TimeUnit.SECONDS);
//2获取锁成功、查询num的值
if(lock){
Object value = redisTemplate.opsForValue().get("num");
//2.1判断num为空return
if(StringUtils.isEmpty(value)){
return;
}
//2.2有值就转成成int
int num = Integer.parseInt(value+"");
//2.3把redis的num加1
redisTemplate.opsForValue().set("num", ++num);
//2.4释放锁,del
//判断比较uuid值是否一样
String lockUuid = (String)redisTemplate.opsForValue().get("lock");
if(lockUuid.equals(uuid)) {
redisTemplate.delete("lock");
}
}else{
//3获取锁失败、每隔0.1秒再获取
try {
Thread.sleep(100);
testLock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
引入:
首先明白abc有的是分布式锁,谁都有机会拿到的 ,假设我们a先拿到这个锁,设置10s过期时间,突然服务器卡住,10s过去之后a的锁已经过期了,假设b拿到锁资源,然后服务器又好了,a又醒了,释放了b的锁资源;
也就是说,一个key有两种释放锁的方式:1、expire 设置的时间过期了 ;2、自己主动释放锁资源;
解决方案:
1:set key uuid nx ex 时间times:(uuid为唯一标识)
2:释放锁的时候需要判断当前的uuid和要释放的uuid是否一样;
也就是说,每个线程将自己拿到的锁释放以后,被其他线程拿到会上uuid,此时就可以将锁资源理解为私有的了;
package com.atguigu.redis_springboot.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* @author diao 2022/3/13
*/
@RestController
@RequestMapping("/redisTest")
public class Test {
@Autowired
private RedisTemplate redisTemplate;
@GetMapping("testLock")
public void testLock(){
String uuid = UUID.randomUUID().toString();
//1、利用redisTemplate获得锁:set key uuid nx ex 时间
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.SECONDS);
if(lock){
//2、获取键值
Object value = redisTemplate.opsForValue().get("num");
//2.1判断value值
if(StringUtils.isEmpty(value)){
return;
}
//2.2value值不为空,先将其转为int类型
int num = Integer.parseInt(value + "");
//+1
redisTemplate.opsForValue().set("num",++num);
//3.释放锁,比较uuid值是否一样
String lockUid = (String) redisTemplate.opsForValue().get("lock");
if(lockUid.equals(uuid)){
//如果uuid一样就删除这个键
redisTemplate.delete("lock");
}
}else{
try {
Thread.sleep(100);
testLock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}