浅谈Redisson实现分布式锁对原理

news2024/11/27 17:37:42

1.Redisson简介

Redis 是最流行的 NoSQL 数据库解决方案之一,而 Java 是世界上最流行(注意,我没有说“最好”)的编程语言之一。虽然两者看起来很自然地在一起“工作”,但是要知道,Redis 其实并没有对 Java 提供原生支持。

相反,作为 Java 开发人员,我们若想在程序中集成 Redis,必须使用 Redis 的第三方库。而 Redisson 就是用于在 Java 程序中操作 Redis 的库,它使得我们可以在程序中轻松地使用 Redis。Redisson 在 java.util 中常用接口的基础上,为我们提供了一系列具有分布式特性的工具类。

Redisson底层采用的是Netty 框架。支持Redis 2.8以上版本,支持Java1.6+以上版本。

2.Redisson实现分布式锁的步骤

2.1.引入依赖

引入重要的两个依赖,一个是spring-boot-starter-data-redis,一个是redisson:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.7.5</version>
</dependency>

2.2.application.properties

# Redis服务器地址(默认session使用)
spring.redis.host=127.0.0.1
# Redis服务器连接密码(默认为空)
# spring.redis.password=
# Redis服务器连接端口
spring.redis.port=6379

2.3.定义Loker

接口编程的思想还是要保持的。我们定义一个Loker接口,用于分布式锁的一些操作:

package com.bruceliu.lock;

/**
 * @BelongsProject: RedissonLock
 * @BelongsPackage: com.bruceliu.lock
 * @Author: bruceliu
 * @QQ:1241488705
 * @CreateTime: 2020-05-08 10:20
 * @Description: 锁接口
 */
import java.util.concurrent.TimeUnit;

public interface Locker {

    /**
     * 获取锁,如果锁不可用,则当前线程处于休眠状态,直到获得锁为止。
     *
     * @param lockKey
     */
    void lock(String lockKey);

    /**
     * 释放锁
     *
     * @param lockKey
     */
    void unlock(String lockKey);

    /**
     * 获取锁,如果锁不可用,则当前线程处于休眠状态,直到获得锁为止。如果获取到锁后,执行结束后解锁或达到超时时间后会自动释放锁
     *
     * @param lockKey
     * @param timeout
     */
    void lock(String lockKey, int timeout);

    /**
     * 获取锁,如果锁不可用,则当前线程处于休眠状态,直到获得锁为止。如果获取到锁后,执行结束后解锁或达到超时时间后会自动释放锁
     *
     * @param lockKey
     * @param unit
     * @param timeout
     */
    void lock(String lockKey, TimeUnit unit, int timeout);

    /**
     * 尝试获取锁,获取到立即返回true,未获取到立即返回false
     *
     * @param lockKey
     * @return
     */
    boolean tryLock(String lockKey);

    /**
     * 尝试获取锁,在等待时间内获取到锁则返回true,否则返回false,如果获取到锁,则要么执行完后程序释放锁,
     * 要么在给定的超时时间leaseTime后释放锁
     *
     * @param lockKey
     * @param waitTime
     * @param leaseTime
     * @param unit
     * @return
     */
    boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit)
            throws InterruptedException;

    /**
     * 锁是否被任意一个线程锁持有
     *
     * @param lockKey
     * @return
     */
    boolean isLocked(String lockKey);
}

有了Locker接口,我们再添加一个基于Redisson的实现类RedissonLocker,实现Locker中的方法:

package com.bruceliu.lock;

/**
 * @BelongsProject: RedissonLock
 * @BelongsPackage: com.bruceliu.lock
 * @Author: bruceliu
 * @QQ:1241488705
 * @CreateTime: 2020-05-08 10:20
 * @Description: 基于Redisson的分布式锁
 */
import java.util.concurrent.TimeUnit;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

public class RedissonLocker implements Locker {

    private RedissonClient redissonClient;

    public RedissonLocker(RedissonClient redissonClient) {
        super();
        this.redissonClient = redissonClient;
    }

    @Override
    public void lock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
    }

    @Override
    public void unlock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.unlock();
    }

    @Override
    public void lock(String lockKey, int leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(leaseTime, TimeUnit.SECONDS);
    }

    @Override
    public void lock(String lockKey, TimeUnit unit, int timeout) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout, unit);
    }

    public void setRedissonClient(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @Override
    public boolean tryLock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        return lock.tryLock();
    }

    @Override
    public boolean tryLock(String lockKey, long waitTime, long leaseTime,
                           TimeUnit unit) throws InterruptedException{
        RLock lock = redissonClient.getLock(lockKey);
        return lock.tryLock(waitTime, leaseTime, unit);
    }

    @Override
    public boolean isLocked(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        return lock.isLocked();
    }

}

2.4.定义工具类

有了Locker和实现类RedissonLocker,我们总不能一直去创建RedissonLocker对象或者不断的在每个要使用到分布式锁的地方都注入RedissonLocker的对象,所以我们定义一个工具类LockUtil,到时候想哪里使用就直接使用工具类的静态方法就行了:

package com.bruceliu.utils;

/**
 * @BelongsProject: RedissonLock
 * @BelongsPackage: com.bruceliu.utils
 * @Author: bruceliu
 * @QQ:1241488705
 * @CreateTime: 2020-05-08 10:21
 * @Description: TODO
 */
import com.bruceliu.lock.Locker;

import java.util.concurrent.TimeUnit;

/**
 * redis分布式锁工具类
 *
 */
public final class LockUtil {

    private static Locker locker;

    /**
     * 设置工具类使用的locker
     * @param locker
     */
    public static void setLocker(Locker locker) {
        LockUtil.locker = locker;
    }

    /**
     * 获取锁
     * @param lockKey
     */
    public static void lock(String lockKey) {
        locker.lock(lockKey);
    }

    /**
     * 释放锁
     * @param lockKey
     */
    public static void unlock(String lockKey) {
        locker.unlock(lockKey);
    }

    /**
     * 获取锁,超时释放
     * @param lockKey
     * @param timeout
     */
    public static void lock(String lockKey, int timeout) {
        locker.lock(lockKey, timeout);
    }

    /**
     * 获取锁,超时释放,指定时间单位
     * @param lockKey
     * @param unit
     * @param timeout
     */
    public static void lock(String lockKey, TimeUnit unit, int timeout) {
        locker.lock(lockKey, unit, timeout);
    }

    /**
     * 尝试获取锁,获取到立即返回true,获取失败立即返回false
     * @param lockKey
     * @return
     */
    public static boolean tryLock(String lockKey) {
        return locker.tryLock(lockKey);
    }

    /**
     * 尝试获取锁,在给定的waitTime时间内尝试,获取到返回true,获取失败返回false,获取到后再给定的leaseTime时间超时释放
     * @param lockKey
     * @param waitTime
     * @param leaseTime
     * @param unit
     * @return
     * @throws InterruptedException
     */
    public static boolean tryLock(String lockKey, long waitTime, long leaseTime,
                                  TimeUnit unit) throws InterruptedException {
        return locker.tryLock(lockKey, waitTime, leaseTime, unit);
    }

    /**
     * 锁释放被任意一个线程持有
     * @param lockKey
     * @return
     */
    public static boolean isLocked(String lockKey) {
        return locker.isLocked(lockKey);
    }
}

2.5.Redisson的配置类

现在我们开始配置吧,创建一个redisson的配置类RedissonConfig,内容如下:

package com.bruceliu.config;

import java.io.IOException;

import com.bruceliu.lock.RedissonLocker;
import com.bruceliu.utils.LockUtil;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @BelongsProject: RedissonLock
 * @BelongsPackage: com.bruceliu.config
 * @Author: bruceliu
 * @QQ:1241488705
 * @CreateTime: 2020-05-08 10:22
 * @Description: TODO
 */
@Configuration
public class RedissonConfig {

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private String port;

    //@Value("${spring.redis.password}")
    //private String password;

    /**
     * RedissonClient,单机模式
     * @return
     * @throws IOException
     */
    @Bean(destroyMethod = "shutdown")
    public RedissonClient redisson() throws IOException {
        Config config = new Config();
        //config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password);
        config.useSingleServer().setAddress("redis://" + host + ":" + port);
        return Redisson.create(config);
    }

    @Bean
    public RedissonLocker redissonLocker(RedissonClient redissonClient){
        RedissonLocker locker = new RedissonLocker(redissonClient);
        //设置LockUtil的锁处理对象
        LockUtil.setLocker(locker);
        return locker;
    }
}

2.6.Redisson分布式锁业务类

package com.bruceliu.service;

import com.bruceliu.utils.LockUtil;

import java.util.concurrent.TimeUnit;

/**
 * @BelongsProject: RedissonLock
 * @BelongsPackage: com.bruceliu.service
 * @Author: bruceliu
 * @QQ:1241488705
 * @CreateTime: 2020-05-08 10:26
 * @Description: TODO
 */
public class SkillService {

    int n = 500;

    public void seckill() {
        //加锁
        LockUtil.lock("resource", TimeUnit.SECONDS,5000);
        try {
            System.out.println(Thread.currentThread().getName() + "获得了锁");
            Thread.sleep(3000);
            System.out.println(--n);
        } catch (Exception e) {
            //异常处理
        }finally{
            //释放锁
            LockUtil.unlock("resource");
            System.out.println(Thread.currentThread().getName() + "释放了锁");
        }
    }
}

2.7.Redisson分布式锁测试

package com.bruceliu.test;

import com.bruceliu.App;
import com.bruceliu.service.SkillService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * @BelongsProject: RedissonLock
 * @BelongsPackage: com.bruceliu.test
 * @Author: bruceliu
 * @QQ:1241488705
 * @CreateTime: 2020-05-08 10:29
 * @Description: TODO
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class TestLock {

    @Test
    public void testLock() throws Exception{
        SkillService service = new SkillService();
        for (int i = 0; i < 10; i++) {
            ThreadA threadA = new ThreadA(service);
            threadA.setName("ThreadNameA->"+i);
            threadA.start();
        }
        Thread.sleep(50000);
    }

}

class ThreadA extends Thread {

    private SkillService skillService;

    public ThreadA(SkillService skillService) {
        this.skillService = skillService;
    }

    @Override
    public void run() {
        skillService.seckill();
    }
}


运行结果:

注释文中加锁代码:

public void seckill() {
    //加锁
    //LockUtil.lock("resource", TimeUnit.SECONDS,5000);
    try {
        System.out.println(Thread.currentThread().getName() + "获得了锁");
        Thread.sleep(3000);
        System.out.println(--n);
    } catch (Exception e) {
        //异常处理
    }finally{
        //释放锁
        //LockUtil.unlock("resource");
        System.out.println(Thread.currentThread().getName() + "释放了锁");
    }
}

运行结果:
在这里插入图片描述
可以看到存在并发的问题!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/364505.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Win10开机输入密码后1分钟左右就蓝屏,一天出现10个不同蓝屏代码,如何解决

环境: DELL3480 Win10 专业版 问题描述: Win10 电脑开机输入密码后1分钟左右突然就蓝屏,一天出现10个不同蓝屏代码 1.BAD_SYSTEM_CONFIG_INFO bug 检查的值为 0x00000074。 此 bug 检查指示注册表中出现错误。 2.PAGE_FAULT_IN_NONPAGED_AREA bug 检查的值为 0x000…

修改redis的配置文件使得windows的图形界面客户端可以连接redis服务器

1. 安装 Redis 依赖 Redis 是基于 C语言编写的&#xff0c;因此首先需要安装 Redis 所需要的 gcc 依赖&#xff1a; yum install -y gcc tcl 2、上传安装文件 将下载好的 redis-6.2.7.tar.gz 安装包上传到虚拟机的任意目录&#xff08;一般推荐上传到 /usr/local/src目录&…

linux集群技术(三)--七层负载均衡-nginx

nginx特点nginx优势、缺点生产架构nginx 7层负载均衡语法示例nginx负载均衡算法测试案例生产案例 1.nginx特点 1. 功能强大,性能卓越,运行稳定。 2. 配置简单灵活。 3. 能够自动剔除工作不正常的后端服务器。 4. 上传文件使用异步模式。client---nginx---web1 web2 web3 lvs同…

uniapp: 基础开发官网文档

1、uniapp官网文档&#xff1a;https://uniapp.dcloud.net.cn/component/2、uView跨端UI组件库&#xff1a;http://v1.uviewui.com/components/intro.html3、lunch-request&#xff08;类似axios的请求库&#xff09;&#xff1a;https://www.quanzhan.co/luch-request/handboo…

图神经网络基础 Graph 图以及python实现

摘要&#xff1a; 本文将介绍图的基本知识、无向图、有向图、邻接矩阵 python实现&#xff1a;度、连通分量、强连通图、弱连通图、图直径、度中心性、特征向量中心性、中介中心性、连接中心性等基本概念。 python计算代码&#xff1a; 先安装依赖&#xff1a; pip install n…

Java-模块化

模块的基本使用 模块使用步骤 创建模块&#xff08;创建模块&#xff0c;创建包&#xff0c;创建类&#xff0c;定义方法&#xff09; -创建两个模块myOne,myTwo在模块的src目录下创建module-info.java的描述性文件&#xff0c;该文件专门定义模块名&#xff0c;访问权限&#…

一文读懂函数编程及其工作原理

微软MVP实验室研究员 马洪喜-微软 MVP 19年研发经验 云计算咨询顾问专家 容器云及基础架构云技术专家 DevOps 及微服务咨询专家 什么是函数编程 我先用通俗的大白话给大家解释一下函数(Functions, Function as a Service, FaaS)的几个要点&#xff0c;这样看后面示例时才不…

跑步带的耳机选择啥样的好、推荐几款跑步专用耳机

我是个比较喜欢运动的人&#xff0c;每天下班都会在小区湖边跑步健身&#xff0c;每次跑步要是少了耳机&#xff0c;那可没什么兴趣跑了&#xff0c;喜欢跑步的时候对着音乐的节奏跑&#xff0c;所以我的耳机基本上是用的比较紧实不易掉落的无线耳机。接下来我来为大家介绍下我…

ChatGPT 可收费的那种产品该如何实现?一点尝试

导读|时隔两个月&#xff0c;勇哥终于把chatGPT生成SQL的功能发布上线了&#xff0c;支持统计分析查询、创建表、数据生成等多种全面的SQL DDL生成能力&#xff0c;本文就和大家聊聊相关功能的使用和背后实现逻辑&#xff0c;并希望相关功能能帮助大家在工作中提升一定的工作效…

手把手搭建springboot项目05-springboot整合Redis及其业务场景

目录前言一、食用步骤1.1 安装步骤1.1.1 客户端安装1.2 添加依赖1.3 修改配置1.4 项目使用1.5 序列化二、应用场景2.1 缓存2.2.分布式锁2.2.1 redis实现2.2.2 使用Redisson 作为分布式锁2.3 全局ID、计数器、限流2.4 购物车2.5 消息队列 (List)2.6 点赞、签到、打卡 (Set)2.7 筛…

如何弄小程序?公司企业可以这样做小程序

公司企业现在对于小程序的需求已经是刚需了&#xff0c;即使已经有官网的情况下&#xff0c;也会考虑再弄一个小程序来做小程序官网。那么公司企业如何弄小程序呢&#xff1f;下面跟大家说说方法。 流程一、找小程序服务商 由于一些公司企业并不像现在的互联网公司企业那样有…

考试系统 (springboot+vue前后端分离)

系统图片 下载链接 地址&#xff1a; http://www.gxcode.top/code 介绍 一款多角色在线培训考试系统&#xff0c;系统集成了用户管理、角色管理、部门管理、题库管理、试题管理、试题导入导出、考试管理、在线考试、错题训练等功能&#xff0c;考试流程完善。 技术栈 Spr…

Java8中@Contended和伪共享

Java8引入了Contented这个新的注解来减少伪共享(False Sharing)的发生。 sun.misc.Contended注解是被设计用来解决伪共享问题的 文章目录1.缓存行2.伪共享(False Sharing)2.1 CPU的缓存机制3.填充(Padding)4.Contended方式4.总结1.缓存行 CPU读取内存数据时并非一次只读一个字…

ATTCK实战系列——红队实战(二)

网络配置 网卡&#xff1a; WEB&#xff1a; PC&#xff1a; DC&#xff1a; IPWEB10.10.10.80&#xff08;内&#xff09;/192.168.111.80&#xff08;外&#xff09;PC10.10.10.201&#xff08;内&#xff09;/192.168.111.201&#xff08;外&#xff09;DC10.10.10.10物理机…

评论字数统计案例、评论回车发布、 Tab 栏切换、验证码倒计时、显示与隐藏密码——DOM事件

目录 一、DOM事件 1. 评论字数统计案例 2. 评论回车发布 3. Tab 栏切换 4. 验证码倒计时 5. 显示与隐藏密码 一、DOM事件 1. 评论字数统计案例 该案例中的显示输入字数及最大字数模块.wrapper .total 刚开始是看不见的&#xff0c;使用的是不透明度&#xff08;opacit…

量化交易-单因子分析-alphalens

1. 数据准备 1.1 计算因子IC重要函数 def get_clean_factor_and_forward_returns(factor,prices,groupbyNone,binning_by_groupFalse,quantiles5,binsNone,periods(1, 5, 10),filter_zscore20,groupby_labelsNone,max_loss0.35,zero_awareFalse,cumulative_returnsTrue)facto…

Nginx优化与防盗链

Nginx优化与防盗链 &#x1f4d2;博客主页&#xff1a; 微笑的段嘉许博客主页 &#x1f4bb;微信公众号&#xff1a;微笑的段嘉许 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐留言&#x1f4dd; &#x1f4cc;本文由微笑的段嘉许原创&#xff01; &#x1f4c…

图文并茂详解NAT协议(含实例分析)

什么是 NAT 协议 我们的计算机要想访问互联网上的信息&#xff0c;就需要一个地址&#xff0c;而且这个地址是大家&#xff08;其他主机&#xff09;所认可的&#xff0c;是公共的&#xff0c;这个地址也叫做公有 IP 地址。 与之相对的&#xff0c;除了公有 IP 地址外&#x…

Python自动化测试框架【Allure-pytest功能特性介绍】

Python自动化测试框架【Allure-pytest功能特性介绍】 目录&#xff1a;导读 前言 生成报告 测试代码 目录结构 Allure特性 Environment Categories Fixtures and Finalizers allure.attach 总结 写在最后 前言 Allure框架是一个灵活的轻量级多语言测试报告工具&am…

【PMP考试最新解读】第七版《PMBOK》应该如何备考?(含最新资料)

PMP新版大纲加入了ACP敏捷管理的内容&#xff0c;而且还不少&#xff0c;敏捷混合题型占到了 50%&#xff0c;前不久官方也发了通知8月启用第七版《PMBOK》&#xff0c;大家都觉得考试难度提升了&#xff0c;我从新考纲考完下来&#xff0c;最开始也被折磨过一段时间&#xff0…