提示:本文中若出现个人观点不对处请指正,谢谢!
目录
一、Redisson是什么
二、使用Redisson
1.引入 Maven 依赖
2.编写自定义配置类
3.实现分布式可重入锁
附加引申
Redisson开源框架是一个Redis的分布式锁的现成实现方案,是Redis的 java 实现的客户端。通过 Netty 支持非阻塞 I/O。
Redisson实现了分布式锁的自动续期机制、锁的互斥自等待机制、锁的可重入加锁与释放锁的机制。
创建一个基础的springboot项目,引入maven依赖。
org.redisson
redisson-spring-boot-starter
3.13.4
这里使用 程序配置, 也可以使用 文件配置。
package com.redissondemo.config;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
/**
* 2022年7月30日23:22:07
*/
@Configuration
public class MyRedissonConfig {
/**
* 对 Redisson 的使用都是通过 RedissonClient 对象
* @return
* @throws IOException
*/
@Bean(destroyMethod="shutdown") // 服务停止后调用 shutdown 方法。
public RedissonClient redisson() throws IOException {
System.out.println("配置类初始加载......");
// 1.创建配置
Config config = new Config();
// 集群模式
// config.useClusterServers().addNodeAddress("127.0.0.1:6379", "127.0.0.1:6378");
// 2.根据 Config 创建出 RedissonClient 实例。
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
return Redisson.create(config);
}
}
@Autowired private RedissonClient redisson; //自动装配RedissonClient RLock lock = redisson.getLock("onelock"); //获取锁 lock.lock(); //加锁 lock.unlock(); //释放锁
实现分布式可重入锁的RestController类
package com.redissondemo.controller;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 2022年7月30日23:27:20
*/
@RestController
@RequestMapping(path = "/mylock")
public class TestMyLock {
@Autowired
private RedissonClient redisson;
@GetMapping("/testOneLock")
public String testOneLock() {
// 1.获取锁,只要锁的名字一样,获取到的锁就是同一把锁。
RLock lock = redisson.getLock("onelock");
System.out.println("-----获得锁lock对象:"+lock);
// 2.加锁
lock.lock();
try {
System.out.println("加锁成功,执行逻辑代码。线程 ID:" + Thread.currentThread().getId());
//while (true){
Thread.sleep(10000);
System.out.println("逻辑代码执行完成!。。。");
//}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 3.解锁
lock.unlock();
System.out.println("Finally处,释放锁成功。线程 ID:" + Thread.currentThread().getId());
}
return "onelock锁测试完成。";
}
}
启动springboot项目,这里思考两个问题:
1. 多个请求同时请求锁时的运行效果,是并发还是等待?
2. 正在使用锁的线程停止服务运行后,锁会如何处理,会释放吗?
我们先来看第一个问题,多个请求同时请求锁时的运行效果,是并发还是等待?
打开浏览器,发出3个请求:http://127.0.0.1:8080/mylock/testOneLock
如图,进行了3次请求,一起请求锁。
IDEA的控制台运行结果如图:
页面3个请求进来后,获得3个锁对象(椭圆框中),3个请求线程分别是67、68、69(方框中)。从控制台截图中看出多个请求同时请求锁时的运行结果是:等待排队顺序执行,而非并发执行。
第一个线程ID 67 加锁成功后,执行逻辑代码等待10s 钟,然后释放锁。第二和第三个线程ID 68和69需等待第一个线程ID 67 释放锁后,才能继续加锁执行逻辑代码。
第二个线程ID 68 加锁成功后,第三个线程ID 69 需要等待第二个线程ID 68 释放锁后,第三个线程ID 69 才能加锁成功,执行逻辑代码。
所以,Redisson 的可重入锁(lock)是阻塞其他线程的,需要等待其他线程释放锁。
现在来看第二个问题:正在使用锁的线程停止服务运行后,锁会如何处理,会释放吗?
从第一个问题的结果可以看出:如果正在使用锁的线程突然停止,而锁不能释放,那么便会形成死锁。从而阻塞其他线程的运行。这显然是不妙的...
现在我们来做个测试,在浏览器访问一次:http://127.0.0.1:8080/mylock/testOneLock请求,然后在IDEA中,此请求还在执行逻辑代码部分(睡眠10s)时停止服务,此时在redis客户端命令窗口中查看锁的情况,如图:
图中,线程ID 73 的请求加锁成功后,被停止了服务。此时在redis客户端命令窗口中查看锁“onelock” 还是加锁状态,重复多次查看后才出现:(empty list or set) 提示,此时锁被释放。因为可重入锁(lock)默认过期时间30s,所以过期后自动释放了锁。
所以,Redisson 可重入锁(lock),在线程服务停止后会释放。
如果代码在逻辑层面出现问题,我在逻辑代码处加入了while (true){},形成一个死循环做了个测试。运行结果如图:
图中,线程ID 67 不断执行等待10s 钟,而不释放锁。 形成了Redisson 可重入锁(lock)的阻塞,导致其他线程请求一直处于等待中。相当于形成了死锁的发生。问题严重了....