来吧,展示!Redis的分布式锁及其实现Redisson的全过程

时间:2022-01-30 23:49:53

前言

分布式锁是控制分布式系统之间同步访问共享资源的一种方式。

在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,这个时候,便需要使用到分布式锁。

什么是分布式锁

1.在分布式环境中使用到的锁就是分布式锁

2.在分布式环境中对不同应用程序操作的共享资源进行加锁就是分布式锁

分布式环境

1.同一个应用下的子应用采用单独部署的方式运行就是分布式

2.只要应用存在跨JVM就是分布式环境

为什么要有分布式锁

JDK中原生锁(Synchronized、Lock)只针对是同一个JVM实例上操作资源而言,对于不同JVM的操作是没办法进行加锁的。

分布式锁的应用场景

只要存在跨JVM操作,并且存在共享资源竞争问题,就是必须使用到分布式锁的场景。

分布式锁有哪些

常见的分布式锁有以下几种:

1.基于数据库(乐观锁)实现分布式锁

2.基于Redis的分布式锁Redisson

3.基于Zookeeper实现分布式锁

基于redis分布式锁原理

获取锁

通过Redis创建一个唯一的key,如果当前线程能创建这个唯一的key,则表示当前线程获取到锁。

释放锁

当删除Redis中的代表锁的唯一key,则表示释放锁。

什么是死锁

在释放锁时出现异常,Redis中代表锁的唯一key未被删除,而其他线程一直在自旋等待并希望能够获取锁,但事实上所有线程都没能获取到锁的情况称为死锁。

如何解决死锁

我们可以通过对Redis中代表锁的唯一Key设置过期时间来避免死锁的发生。

如何避免锁被其他线程释放

创建锁时记录线程ID,自己的锁只能自己释放。

如何保证锁的可重入性

当线程获取到锁后在Redis中把当前线程的ID做为key的值进行存储,加锁时判断当前线程与Redis锁的值是否一致。

基于redis分布式锁Redisson

配置pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>site.yangpan</groupId>
<artifactId>yangpan-spring-boot</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent> <groupId>site.yangpan.redission</groupId>
<artifactId>yangpan-spring-boot-redission</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>yangpan-spring-boot-redission</name>
<description>Demo project for Spring Boot</description> <properties>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <!--下面这两个依赖就是集成redission的依赖-->
<!--第一个依赖与spring boot版本有关系,参考官方文档-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data-21</artifactId>
<version>3.13.3</version>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.13.3</version>
</dependency>
</dependencies> </project>

注意:网上很多都是直接引入redission依赖,但是我这里是通过Spring Boot Starter的方式引入

配置application.properties

# 公共spring boot配置
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=
#spring.redis.cluster.nodes=
#spring.redis.sentinel.master=
#spring.redis.sentinel.nodes= # Redisson 配置
spring.redis.redisson.config=classpath:redisson.yaml

配置redisson.yaml

singleServerConfig:
idleConnectionTimeout: 10000
pingTimeout: 1000
# connectTimeout: 10000
timeout: 3000
retryAttempts: 3
retryInterval: 1500
# reconnectionTimeout: 3000
# failedAttempts: 3
password:
subscriptionsPerConnection: 5
clientName:
address: "redis://127.0.0.1:6379"
subscriptionConnectionMinimumIdleSize: 1
subscriptionConnectionPoolSize: 50
connectionMinimumIdleSize: 32
connectionPoolSize: 64
database: 0
# dnsMonitoring: false
dnsMonitoringInterval: 5000
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
#"transportMode":"NIO"

编写RedissonSpringDataConfig

接下来我们注册 RedissonConnectionFactory 到 Spring context

package site.yangpan.redission.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.spring.data.connection.RedissonConnectionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource; import java.io.IOException; /**
* 注册 RedissonConnectionFactory 到 Spring context
* https://github.com/redisson/redisson/tree/master/redisson-spring-data#spring-data-redis-integration
* Created by yangpan on 2020-08-29 19:15.
*/
@Configuration
public class RedissonSpringDataConfig { @Bean
public RedissonConnectionFactory redissonConnectionFactory(RedissonClient redisson) {
return new RedissonConnectionFactory(redisson);
} @Bean(destroyMethod = "shutdown")
public RedissonClient redisson(@Value("classpath:/redisson.yaml") Resource configFile) throws IOException {
Config config = Config.fromYAML(configFile.getInputStream());
return Redisson.create(config);
} }

使用Redisson

这里我们直接编写一个controller,然后使用Redisson

package site.yangpan.redission.reentrantLock;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
* 基于Redis的Redisson分布式可重入锁RLock
* Java对象实现了java.util.concurrent.locks.Lock接口。
* 同时还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。
* Created by yangpan on 2020-08-29 19:40.
*/
@Controller
@RestController
@RequestMapping("/redissionReentrantLock")
public class RedissonReentrantLock { private Integer stock = 100; @Autowired
private RedissonClient redissonClient; @GetMapping("/test")
public void test(){
//使用线程池模拟并发,看分布式锁有没有问题
ExecutorService executorService = Executors.newFixedThreadPool(8);
for(int i=0;i<=1000;i++){
executorService.execute(() -> {
try {
Thread.sleep(3000);
//调用加锁方法
reduceStockRessionLock();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
} /**
* 加锁情况
*/
private void reduceStockRessionLock() {
//获取锁(可重入锁)
RLock lock = redissonClient.getLock("anyLock"); //加锁
lock.lock(); //业务操作
if(stock > 0){
stock--;
System.out.println("当前库存剩余:" + stock);
} //释放锁
lock.unlock();
} /**
* 不加锁情况
*/
private void reduceStock() throws InterruptedException {
//业务操作
if(stock > 0){
stock--;
System.out.println("当前库存剩余:" + stock);
}
} }

启动测试

接下来我们启动项目,访问接口,查看日志,观察日志分析可得Redisson分布式锁确实起到了作用

curl http://localhost:8080/redissionReentrantLock/test

最后

感谢你看到这里,看完有什么的不懂的可以在评论区问我,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

来吧,展示!Redis的分布式锁及其实现Redisson的全过程的更多相关文章

  1. 基于redis的分布式锁实现方案--redisson

    实例代码地址,请前往:https://gitee.com/GuoqingLee/distributed-seckill redis官方文档地址,请前往:http://www.redis.cn/topi ...

  2. 基于redis的分布式锁的分析与实践

    ​ 前言:在分布式环境中,我们经常使用锁来进行并发控制,锁可分为乐观锁和悲观锁,基于数据库版本戳的实现是乐观锁,基于redis或zookeeper的实现可认为是悲观锁了.乐观锁和悲观锁最根本的区别在于 ...

  3. 不用找了,基于 Redis 的分布式锁实战来了!

    Java技术栈 www.javastack.cn 优秀的Java技术公众号 作者:菜蚜 my.oschina.net/wnjustdoit/blog/1606215 前言:在分布式环境中,我们经常使用 ...

  4. redis咋么实现分布式锁,redis分布式锁的实现方式,redis做分布式锁 积极正义的少年

    前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...

  5. Redis实现分布式锁的正确姿势

    分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介绍Re ...

  6. Redis单机版分布式锁实现

    转载自:https://www.cnblogs.com/linjiqin/p/8003838.html Redis分布式锁的正确实现方式 前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基 ...

  7. Redis实现分布式锁的正确使用方式&lpar;java版本&rpar;

    Redis实现分布式锁的正确使用方式(java版本) 本文使用第三方开源组件Jedis实现Redis客户端,且只考虑Redis服务端单机部署的场景. 分布式锁一般有三种实现方式: 1. 数据库乐观锁: ...

  8. redis实现分布式锁 转自importnew 记录一下

    前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...

  9. Redis 实现分布式锁

    前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...

随机推荐

  1. parawork平台介绍

     ParaWork软件项目估算平台---科学估算项目,让管理更简单 ParaWork软件项目估算平台是由北京软件造价评估技术创新联盟与北京科信深度科技有限公司联合开发.维护的软件项目估算工具,为会员免 ...

  2. java—数组乘积输入: 一个长度为n的整数数组input 输出: 一个长度为n的数组result,满足result&lbrack;i&rsqb; &equals; input数组中,除了input&lbrack;i&rsqb; 之外的所有数的乘积,不用考虑溢出例如 input &lbrace;2&comma; 3&comma; 4&comma; 5&rcub; output&colon; &lbrace;60&comma; 40&comma; 30&comma; 24&rcub;

    /** * 小米关于小米笔试题 数组乘积输入: 一个长度为n的整数数组input 输出: 一个长度为n的数组result,满足result[i] = * input数组中,除了input[i] 之外的 ...

  3. &num;你好Unity3D&num;Hierarchy视图监听gameObject点击事件

    今天无意间又找到了个好方法     1 2 3 4 5 6 7 8 9 10 [InitializeOnLoadMethod] static void Start () {   Selection.s ...

  4. 图像的影像地图超链接,&lt&semi;map&gt&semi;标签浅谈

    在HTML中还可以把图片划分成多个热点区域,每一个热点域链接到不同网页的资源.这种效果的实质是把一幅图片划分为不同的热点区域,再让不同的区域进行超链接.这就是影像地图.要完成地图区域超链接要用到三种标 ...

  5. SugarSync的API总结

    SugarSync API App支持SugarSync网盘的前提: 1.AccessKeyID:xxx 2.Private Access Key:xxx 3.AppID:xxx 详细的API总结如下 ...

  6. Java 浅析Thread&period;join&lpar;&rpar;

    概要 本文分为三部分对 Thread.join() 进行分析: 1. join() 的示例和作用 2. join() 源码分析 3. 对网上其他分析 join() 的文章提出疑问 1. join() ...

  7. gcc编译出现dlopen、dlerror、dlsym、dlcolse的解决方法

      ➜  test_sqlite3 gcc *.c -I . -o xixi -pthread      /tmp/cckGKTrr.o: In function `unixDlOpen': sqli ...

  8. 前端 - jquery方式 &sol; iframe &plus;form 方式 上传文件

    环境与上一章一样 jquery 方式上传文件: HTML代码 {#html代码开始#} <input type="file" id="img" > ...

  9. 文件操作(十二)——open&comma;read&comma;close&comma;write&comma;seek&comma;truncate

    open函数 #!/usr/bin/env python #-*- coding:utf8 -*- f = open('xxx','r',encoding='utf-8') data = f.read ...

  10. Code Signal&lowbar;练习题&lowbar;reverseParentheses

    You have a string s that consists of English letters, punctuation marks, whitespace characters, and ...