Redis分布式鎖
redis1.0版本的鎖(有bug版本的分布式鎖)
@GetMapping("/Redis1/{id}")
public String RedisLock1(@PathVariable("id") String id){
String LockName="LockName";
Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(LockName, "shuxiaosheng",10,TimeUnit.SECONDS);
if (lock!=null&&!lock){
return "系統繁忙,請稍微再試";
}
try {
Integer stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if (stock>0){
Integer realStock=stock-1;
stringRedisTemplate.opsForValue().set("stock",realStock.toString());
}
}finally{
stringRedisTemplate.delete(LockName);
}
return "end";
}
模擬因處理業務時間過長而導致的分布式鎖失效的情況
@GetMapping("/Redis1/{id}")
public String RedisLock1Exception(@PathVariable("id") Integer id) throws Exception{
String LockName="LockName";
Boolean shuxiaosheng = stringRedisTemplate.opsForValue().setIfAbsent(LockName, "線程"+id,10,TimeUnit.SECONDS);
if (shuxiaosheng!=null&&!shuxiaosheng){
return "系統繁忙,請稍微再試";
}
try {
//線程暫停超過十秒,鎖就自動釋放了
if(1==id){
Thread.sleep(15000);
System.out.println("線程1暫停15秒,鎖已經自動釋放");
}
if(2==id){
Thread.sleep(8000);
System.out.println("線程2暫停8秒");
}
Integer stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if (stock>0){
Integer realStock=stock-1;
stringRedisTemplate.opsForValue().set("stock",realStock.toString());
}
}finally{
System.out.println("當前線程是:"+id+"釋放的鎖是:"+stringRedisTemplate.opsForValue().get(LockName));
stringRedisTemplate.delete(LockName);
}
return "end";
}
1.0版本的存在的缺陷,例如:
- 當線程1進來,執行業務代碼需要15秒.
- 當線程2進來,執行業務代碼需要8秒.
- 線程1最先進來,拿到了鎖,當線程1執行業務代碼到11秒時,鎖被自動釋放了.
- 線程2立馬進來,拿到了鎖,當線程2執行業務代碼到4秒時,這時,線程1業務代碼已經執行結束,并且手動釋放了線程2加的鎖,
控制臺打印
線程1暫停15秒,鎖已經自動釋放
當前線程是:1釋放的鎖是:線程2
線程2暫停8秒
當前線程是:2釋放的鎖是:null
總結:
因為高并發,線程1執行業務的代碼時間大于鎖超時時間,線程1的鎖自動釋放了,線程2加入進來,結果線程1執行完畢,釋放了線程2加的鎖,出現問題
redis2.0版本的鎖(適用于并發一般的軟件公司)
@GetMapping("/Redis2/{id}")
public String RedisLock12(@PathVariable("id") Integer id) throws Exception{
String LockName="LockName";
String uuid = UUID.randomUUID().toString();
Boolean shuxiaosheng = stringRedisTemplate.opsForValue().setIfAbsent(LockName, uuid+"線程"+id,10,TimeUnit.SECONDS);
if (shuxiaosheng!=null&&!shuxiaosheng){
return "系統繁忙,請稍微再試";
}
try {
//線程暫停十秒,鎖就自動釋放了
if(1==id){
Thread.sleep(15000);
System.out.println("線程1暫停15秒,鎖已經自動釋放");
}
if(2==id){
Thread.sleep(8000);
System.out.println("線程2暫停8秒");
}
Integer stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if (stock>0){
Integer realStock=stock-1;
stringRedisTemplate.opsForValue().set("stock",realStock.toString());
}
}finally{
//當前線程只能釋放自己加的鎖
if ((uuid+"線程"+id).equals(stringRedisTemplate.opsForValue().get(LockName))){
System.out.println("當前線程是:"+id+"釋放的鎖是:"+stringRedisTemplate.opsForValue().get(LockName));
stringRedisTemplate.delete(LockName);
}
}
return "end";
}
控制臺打印
線程1暫停15秒,鎖已經自動釋放
線程2暫停8秒
當前線程是:2釋放的鎖是:c579e42a-ecfb-40a4-8e5f-689f3d858f40線程2
總結
redis2.0相比較與redis1.0,就加了個核心判斷,當前線程只能釋放自己加的鎖,不能釋放別人的鎖
到此簡單的redis鎖就已經實現了
redis3.0版本的鎖(適用并發比較高互聯網公司)
添加依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.4.1</version>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.13.6</version>
</dependency>
添加配合類
@Configuration
public class MyRedissonConfig {
@Bean(destroyMethod="shutdown")
RedissonClient redisson() throws IOException {
//1、創建配置
Config config = new Config();
config.useSingleServer()
.setAddress("127.0.0.1:6379");
return Redisson.create(config);
}
}
實現
@RequestMapping("/redisson")
public String testRedisson(){
//獲取分布式鎖,只要鎖的名字一樣,就是同一把鎖
RLock lock = redissonClient.getLock("lock");
//加鎖(阻塞等待),默認過期時間是30秒
lock.lock();
try{
//如果業務執行過長,Redisson會自動給鎖續期
Thread.sleep(1000);
System.out.println("加鎖成功,執行業務邏輯");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//解鎖,如果業務執行完成,就不會繼續續期,即使沒有手動釋放鎖,在30秒過后,也會釋放鎖
lock.unlock();
}
return "Hello Redisson!";
}
原理
大家都知道,如果剛上完鎖,負責存儲這個分布式的Redisson節點宕機后,這樣就出現了死鎖,為了避免這種情況發生,Redisson內部提供了一個監控鎖的看門狗,他的作用是:如果當前線程還有持有鎖,且鎖快要到期了(默認鎖是30秒),就繼續延長鎖的時間,如果當前線程掛了,那么就不會自動延長鎖,到期之后,鎖會自動釋放
浙公網安備 33010602011771號