提示:本文中若出现个人观点不对处请指正,谢谢!


一、Redisson是什么

Redisson开源框架是一个Redis的分布式锁的现成实现方案,是Redis的 java 实现的客户端。通过 Netty 支持非阻塞 I/O。

Redisson实现了分布式锁的自动续期机制、锁的互斥自等待机制、锁的可重入加锁与释放锁的机制。

二、使用Redisson

1.引入 Maven 依赖

创建一个基础的springboot项目,引入maven依赖。 

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.13.4</version>
        </dependency>

2.编写自定义配置类

这里使用 程序配置, 也可以使用 文件配置。

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);
    }
}

3.实现分布式可重入锁

@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)的阻塞,导致其他线程请求一直处于等待中。相当于形成了死锁的发生。问题严重了....

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐