前言
分布式锁是管制分布式系统之间同步访问共享资源的一种形式。
在分布式系统中,经常须要协调他们的动作。如果不同的零碎或是同一个零碎的不同主机之间共享了一个或一组资源,那么拜访这些资源的时候,往往须要互斥来避免彼此烦扰来保障一致性,这个时候,便须要应用到分布式锁。
什么是分布式锁
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 相干技术文章或行业资讯,欢送大家关注和转发文章!