springboot(十二)-分布式锁(redis)
什么是分布式锁?
要介绍分布式锁,首先要提到与分布式锁相对应的是线程锁、进程锁。
线程锁:主要用来给方法、代码块加锁。当某个方法或代码使用锁,在同一时刻仅有一个线程执行该方法或该代码段。线程锁只在同一JVM中有效果,因为线程锁的实现在根本上是依靠线程之间共享内存实现的,比如synchronized是共享对象头,显示锁Lock是共享某个变量(state)。
进程锁:为了控制同一操作系统中多个进程访问某个共享资源,因为进程具有独立性,各个进程无法访问其他进程的资源,因此无法通过synchronized等线程锁实现进程锁。
分布式锁:当多个进程不在同一个系统中,用分布式锁控制多个进程对资源的访问。
分布式锁的使用场景
线程间并发问题和进程间并发问题都是可以通过分布式锁解决的,但是强烈不建议这样做!因为采用分布式锁解决这些小问题是非常消耗资源的!分布式锁应该用来解决分布式情况下的多进程并发问题才是最合适的。
有这样一个情境,线程A和线程B都共享某个变量X。
如果是单机情况下(单JVM),线程之间共享内存,只要使用线程锁就可以解决并发问题。
如果是分布式情况下(多JVM),线程A和线程B很可能不是在同一JVM中,这样线程锁就无法起到作用了,这时候就要用到分布式锁来解决。
分布式锁简介
其实Java世界的”半壁江山”——Spring早就提供了分布式锁的实现。早期,分布式锁的相关代码存在于Spring Cloud的子项目Spring Cloud Cluster中,后来被迁到Spring Integration中。
可能有不少童鞋对Spring Integration不是很熟悉,简单介绍一下——官方说法,这是一个 企业集成模式
的实现;通俗地说,Spring Integration的定位是一个轻量级的ESB,尽管它做了很多ESB不做的事情。顺便说一下,Spring Cloud Stream的底层也是Spring Integration。
Spring Integration提供的全局锁目前为如下存储提供了实现:
- Gemfire
- JDBC
- Redis
- Zookeeper
它们使用相同的API抽象——这正是Spring最擅长的。这意味着,不论使用哪种存储,你的编码体验是一样的,有一天想更换实现,只需要修改依赖和配置就可以了,无需修改代码。
编码
新建一个sprinboot项目,然后配置相关内容和测试代码。
1.pom.xml
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.itmuch.cloud</groupId>
<artifactId>redisLock</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>redisLock</name>
<url>http://maven.apache.org</url> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> <!-- 这个需要为 true 热部署才有效 -->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>
这里提一下,我之前都是用的1.5.9.RELEASE版本的springboot。但我发现添加了上面红色字体的三个依赖之后,发现编译不通过,发聩的信息是版本问题,后来我改为2.0.0.RELEASE版本,OK了。
2.application.yml
server:
port: 8080
spring:
redis:
port: 6379
host: localhost
当前这个应用的端口,我们设置为8080,然后Redis服务器的端口当然是默认的6379啦!
3.RedisLockConfiguration.java
@Configuration
public class RedisLockConfiguration {
@Bean
public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {
return new RedisLockRegistry(redisConnectionFactory, "spring-cloud");
}
}
这个很好理解,我们要用到锁,当然得先注册一个锁的Bean对象到spring容器中,以便获取使用。
4.testController.java
@RestController
@RequestMapping(value = "index")
public class testController {
private final static Logger log = LoggerFactory.getLogger(testController.class);
@Autowired
private RedisLockRegistry redisLockRegistry;
@GetMapping("test")
public void test() throws InterruptedException {
Lock lock = redisLockRegistry.obtain("lock");
boolean b1 = lock.tryLock(3, TimeUnit.SECONDS);
log.info("b1 is : {}", b1); TimeUnit.SECONDS.sleep(5); boolean b2 = lock.tryLock(3, TimeUnit.SECONDS);
log.info("b2 is : {}", b2); lock.unlock();
lock.unlock();
}
这个接口类中的代码如果不太明白,不,不管你明不明白,都建议看一下org.springframework.integration.redis.util.RedisLockRegistry这个类的注释。
/**
* Implementation of {@link LockRegistry} providing a distributed lock using Redis.
* Locks are stored under the key {@code registryKey:lockKey}. Locks expire after
* (default 60) seconds. Threads unlocking an
* expired lock will get an {@link IllegalStateException}. This should be
* considered as a critical error because it is possible the protected
* resources were compromised.
* <p>
* Locks are reentrant.
* <p>
* <b>However, locks are scoped by the registry; a lock from a different registry with the
* same key (even if the registry uses the same 'registryKey') are different
* locks, and the second cannot be acquired by the same thread while the first is
* locked.</b>
* <p>
* <b>Note: This is not intended for low latency applications.</b> It is intended
* for resource locking across multiple JVMs.
* <p>
* {@link Condition}s are not supported.
测试
这样,我们一个工程就开发完了,你可以再复制一份工程,RedisLock2.只要把端口号改了就行,比如改为8081.然后同时启动俩工程。
我一般都是一个放在eclipse中跑,一个在终端通过命令行启动,这样简洁一点。
接下来,你打开两个网页,输好地址,然后快速依次访问两个工程的接口:
http://localhost:8081/index/test
http://localhost:8080/index/test
然后看两个控制台的结果
先看第一个:两个都是true,说明同一个线程是可以获得到锁的,正如上面注释
Locks are reentrant.
再看第二个:第一次是false,因为上一个线程锁住了,还没有释放,所以它是获取不到的。而第二次返回true,说明获得到了,因为第一个工程中跑的线程已经释放了锁。
另外,你如果要看到现象,你开启RedisClient.你访问其中一个工程的接口,快速刷新redis对应的db(我的是db0)
你不停的刷新db0,你会看到这个spring-cloud的锁key,过几秒就消失了,因为被释放了嘛。这也证实了我们的运行结果。
如果你代码中不释放锁,那么这个spring-cloud的锁key过60秒会自动消失,正如上面注释所描述的那样。
代码下载地址:https://gitee.com/fengyuduke/my_open_resources/blob/master/redisLock.zip
springboot(十二)-分布式锁(redis)的更多相关文章
- [转帖]SpringBoot集成redisson分布式锁
SpringBoot集成redisson分布式锁 https://www.cnblogs.com/yangzhilong/p/7605807.html 前几天同事刚让增加上这一块东西. 百度查一下 啥 ...
- Java进阶专题(二十五) 分布式锁原理与实现
前言 现如今很多系统都会基于分布式或微服务思想完成对系统的架构设计.那么在这一个系统中,就会存在若干个微服务,而且服务间也会产生相互通信调用.那么既然产生了服务调用,就必然会存在服务调用延迟或失败 ...
- Redis系列(二)--分布式锁、分布式ID简单实现及思路
分布式锁: Redis可以实现分布式锁,只是讨论Redis的实现思路,而真的实现分布式锁,Zookeeper更加可靠 为什么使用分布式锁: 单机环境下只存在多线程,通过同步操作就可以实现对并发环境的安 ...
- springboot整合redisson分布式锁
一.通过maven引入redisson的jar包 <dependency> <groupId>org.redisson</groupId> <artifact ...
- spring-boot-route(十二)整合redis做为缓存
redis简介 redis作为一种非关系型数据库,读写非常快,应用十分广泛,它采用key-value的形式存储数据,value常用的五大数据类型有string(字符串),list(链表),set(集合 ...
- SpringBoot集成redisson分布式锁
官方文档:https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95 20180226更新:增加tryLock方法,建议后面去掉Distr ...
- 基于Redisson+SpringBoot的Redission分布式锁
原文:https://blog.csdn.net/sunct/article/details/80178197 定义分布式锁接口 package com.redis.lock.redisson_spr ...
- 【面试 redis】【第十二篇】redis的相关面试问题
redis的相关面试问题 redis教程:http://www.redis.net.cn/tutorial/3501.html ==================================== ...
- 分布式锁--Redis小试牛刀
参考文章: Redis分布式锁的正确实现方式 分布式锁看这篇就够了 在这两篇文章的指引下亲测 Redis分布式锁 引言 分布式系统一定会存在CAP权衡问题,所以才会出现分布式锁 什么是CAP理论? 为 ...
随机推荐
- [原创]解读2017 OWASP Top10漏洞体系(含接口安全)
2017年4月初,OWASP发布了关于Top10的征求意见版. 争议最大的是A7攻击检测与防范不足. 但我主要是按照日常的渗透漏洞进行解读分析的. 解读完毕后,首发t00ls原创文章. https:/ ...
- Android Studio修改默认Activity继承AppCompatActivity
在Android Studio中新建Activity默认继承AppCompatActivity,感觉这点十分不爽,找了很久,终于发现在android Studio安装目录下有个模板文件,修改其中的参数 ...
- Linux服务器上如何设置MySQL的max_allowed_packe
mysql根据配置文件会限制server接受的数据包大小. 有时候大的插入和更新会被max_allowed_packet 参数限制掉,导致失败. 查看目前配置 show VARIABLES like ...
- Jenkins执行selenium报错unknown error: cannot find Chrome binary
问题描述:在Pycharm中执行selenium测试用例,可以正常运行, 集成在Jenkins中,构建时,发现构建成功,但是查看Console Output,报错:unknown error: can ...
- m2e 插件
官网 http://www.eclipse.org/m2e/ 在线安装地址 http://download.eclipse.org/technology/m2e/releases 插件简介 Launc ...
- linux每天一小步---alias命令详解
1 命令功能 alias命令用来设置指令的别名,alias命令设置的别名只限于该次登陆操作,若要每次登入即自动设好别名,可在/etc/profile或自己的~/.bashrc中设定指令的别名. ...
- fakeapp, faceswap, deepfacelab等deepfakes换脸程序的简单对比
https://deepfakes.com.cn/index.php/95.html https://www.cnblogs.com/zackstang/p/9011753.html
- C# FTPClientHelper共公类 实现文件上传,目录操作,下载等动作
文档说明 本文档使用Socket通信方式来实现ftp文件的上传下载等命令的执行 1.基本介绍 由于最近的项目是客户端的程序,需要将客户端的图片文件[切图]-[打包]-[ftp上传],现在就差最后一步了 ...
- Hdu1874 畅通工程续 2017-04-12 18:37 48人阅读 评论(0) 收藏
畅通工程续 Time Limit : 3000/1000ms (Java/Other) Memory Limit : 32768/32768K (Java/Other) Total Submiss ...
- Python入门基础学习 一
Python入门基础学习 一 Python下载及安装 下载地址:https://www.python.org/,选择最新的版本下载 稍等一会,安装完成. 简单语句 从idle启动Python:IDLE ...