前言

面试总是会被问到有没有用过分布式锁、redis 锁,大部分读者平时很少接触到,所以只能很无奈的回答 “没有”。本文通过 Spring Boot 整合 redisson 来实现分布式锁,并结合 demo 测试结果。

首先看下大佬总结的图

正文

添加依赖

<!--redis-->
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-data-redis
</artifactId>
</dependency>
<!--redisson-->
<dependency>
<groupId>
org.redisson
</groupId>
<artifactId>
redisson-spring-boot-starter
</artifactId>
<version>
3.10.6
</version>
</dependency>

配置信息

spring:
# redis
redis:
host:
47.103
.
5.190
port:
6379
jedis:
pool:
# 连接池最大连接数(使用负值表示没有限制)
max-active: 100
# 连接池中的最小空闲连接
max-idle: 10
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1
# 连接超时时间(毫秒)
timeout: 5000
#默认是索引为0的数据库
database: 0

配置类

/**
* redisson 配置,下面是单节点配置:
*
* @author gourd
*/
@Configuration
publicclass
RedissonConfig
{
@Value
(
"${spring.redis.host}"
)
private
String
host;
@Value
(
"${spring.redis.port}"
)
private
String
port;
@Value
(
"${spring.redis.password:}"
)
private
String
password; @Bean
public
RedissonClient
redissonClient() {
Config
config =
new
Config
();
//单节点
config.useSingleServer().setAddress(
"redis://"
+ host +
":"
+ port);
if
(
StringUtils
.isEmpty(password)) {
config.useSingleServer().setPassword(
null
);
}
else
{
config.useSingleServer().setPassword(password);
}
//添加主从配置
// config.useMasterSlaveServers().setMasterAddress("").setPassword("").addSlaveAddress(new String[]{"",""});
// 集群模式配置 setScanInterval()扫描间隔时间,单位是毫秒, //可以用"rediss://"来启用SSL连接
// config.useClusterServers().setScanInterval(2000).addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001").addNodeAddress("redis://127.0.0.1:7002");
return
Redisson
.create(config);
}
}

Redisson 工具类

/**
* redis分布式锁帮助类
*
* @author gourd
*
*/
publicclass
RedisLockUtil
{ privatestatic
DistributedLocker
distributedLocker =
SpringContextHolder
.getBean(
"distributedLocker"
,
DistributedLocker
.
class
); /**
* 加锁
* @param lockKey
* @return
*/
publicstatic
RLock
lock
(
String
lockKey) {
return
distributedLocker.
lock
(lockKey);
} /**
* 释放锁
* @param lockKey
*/
publicstaticvoid
unlock(
String
lockKey) {
distributedLocker.unlock(lockKey);
} /**
* 释放锁
* @param lock
*/
publicstaticvoid
unlock(
RLock
lock
) {
distributedLocker.unlock(
lock
);
} /**
* 带超时的锁
* @param lockKey
* @param timeout 超时时间 单位:秒
*/
publicstatic
RLock
lock
(
String
lockKey,
int
timeout) {
return
distributedLocker.
lock
(lockKey, timeout);
} /**
* 带超时的锁
* @param lockKey
* @param unit 时间单位
* @param timeout 超时时间
*/
publicstatic
RLock
lock
(
String
lockKey,
int
timeout,
TimeUnit
unit ) {
return
distributedLocker.
lock
(lockKey, unit, timeout);
} /**
* 尝试获取锁
* @param lockKey
* @param waitTime 最多等待时间
* @param leaseTime 上锁后自动释放锁时间
* @return
*/
publicstaticboolean
tryLock(
String
lockKey,
int
waitTime,
int
leaseTime) {
return
distributedLocker.tryLock(lockKey,
TimeUnit
.SECONDS, waitTime, leaseTime);
} /**
* 尝试获取锁
* @param lockKey
* @param unit 时间单位
* @param waitTime 最多等待时间
* @param leaseTime 上锁后自动释放锁时间
* @return
*/
publicstaticboolean
tryLock(
String
lockKey,
TimeUnit
unit,
int
waitTime,
int
leaseTime) {
return
distributedLocker.tryLock(lockKey, unit, waitTime, leaseTime);
} /**
* 获取计数器
*
* @param name
* @return
*/
publicstatic
RCountDownLatch
getCountDownLatch(
String
name){
return
distributedLocker.getCountDownLatch(name);
} /**
* 获取信号量
*
* @param name
* @return
*/
publicstatic
RSemaphore
getSemaphore(
String
name){
return
distributedLocker.getSemaphore(name);
}
}

底层封装

/**
* @author gourd
*/
publicinterface
DistributedLocker
{ RLock
lock
(
String
lockKey); RLock
lock
(
String
lockKey,
int
timeout); RLock
lock
(
String
lockKey,
TimeUnit
unit,
int
timeout); boolean
tryLock(
String
lockKey,
TimeUnit
unit,
int
waitTime,
int
leaseTime); void
unlock(
String
lockKey); void
unlock(
RLock
lock
);
} /**
* @author gourd
*/
@Component
publicclass
RedisDistributedLocker
implements
DistributedLocker
{ @Autowired
private
RedissonClient
redissonClient; @Override
public
RLock
lock
(
String
lockKey) {
RLock
lock
= redissonClient.getLock(lockKey);
lock
.
lock
();
returnlock
;
} @Override
public
RLock
lock
(
String
lockKey,
int
leaseTime) {
RLock
lock
= redissonClient.getLock(lockKey);
lock
.
lock
(leaseTime,
TimeUnit
.SECONDS);
returnlock
;
} @Override
public
RLock
lock
(
String
lockKey,
TimeUnit
unit ,
int
timeout) {
RLock
lock
= redissonClient.getLock(lockKey);
lock
.
lock
(timeout, unit);
returnlock
;
} @Override
publicboolean
tryLock(
String
lockKey,
TimeUnit
unit,
int
waitTime,
int
leaseTime) {
RLock
lock
= redissonClient.getLock(lockKey);
try
{
returnlock
.tryLock(waitTime, leaseTime, unit);
}
catch
(
InterruptedException
e) {
returnfalse
;
}
} @Override
publicvoid
unlock(
String
lockKey) {
RLock
lock
= redissonClient.getLock(lockKey);
lock
.unlock();
} @Override
publicvoid
unlock(
RLock
lock
) {
lock
.unlock();
}
}

测试

模拟并发测试

/**
* redis分布式锁控制器
* @author gourd
* @since 2019-07-30
*/
@RestController
@Api
(tags =
"redisson"
, description =
"redis分布式锁控制器"
)
@RequestMapping
(
"/redisson"
)
@Slf4j
publicclass
RedissonLockController
{ /**
* 锁测试共享变量
*/
private
Integer
lockCount =
10
; /**
* 无锁测试共享变量
*/
private
Integer
count =
10
; /**
* 模拟线程数
*/
privatestaticint
threadNum =
10
; /**
* 模拟并发测试加锁和不加锁
* @return
*/
@GetMapping
(
"/test"
)
@ApiOperation
(value =
"模拟并发测试加锁和不加锁"
)
publicvoidlock
(){
// 计数器
final
CountDownLatch
countDownLatch =
new
CountDownLatch
(
1
);
for
(
int
i =
0
; i < threadNum; i ++) {
MyRunnable
myRunnable =
new
MyRunnable
(countDownLatch);
Thread
myThread =
new
Thread
(myRunnable);
myThread.start();
}
// 释放所有线程
countDownLatch.countDown();
} /**
* 加锁测试
*/
privatevoid
testLockCount() {
String
lockKey =
"lock-test"
;
try
{
// 加锁,设置超时时间2s
RedisLockUtil
.
lock
(lockKey,
2
,
TimeUnit
.SECONDS);
lockCount--;
log.info(
"lockCount值:"
+lockCount);
}
catch
(
Exception
e){
log.error(e.getMessage(),e);
}
finally
{
// 释放锁
RedisLockUtil
.unlock(lockKey);
}
} /**
* 无锁测试
*/
privatevoid
testCount() {
count--;
log.info(
"count值:"
+count);
} publicclass
MyRunnable
implements
Runnable
{
/**
* 计数器
*/
final
CountDownLatch
countDownLatch; public
MyRunnable
(
CountDownLatch
countDownLatch) {
this
.countDownLatch = countDownLatch;
} @Override
publicvoid
run() {
try
{
// 阻塞当前线程,直到计时器的值为0
countDownLatch.
await
();
}
catch
(
InterruptedException
e) {
log.error(e.getMessage(),e);
}
// 无锁操作
testCount();
// 加锁操作
testLockCount();
} } }

调用接口后打印值:

测试结果

根据打印结果可以明显看到,未加锁的 count-- 后值是乱序的,而加锁后的结果和我们预期的一样。

由于条件问题没办法测试分布式的并发。只能模拟单服务的这种并发,但是原理是一样,希望对大家有帮助。如有错误之处,欢迎指正。

最后

私信回复 资料 领取一线大厂Java面试题总结+各知识点学习思维导+一份300页pdf文档的Java核心知识点总结!

这些资料的内容都是面试时面试官必问的知识点,篇章包括了很多知识点,其中包括了有基础知识、Java集合、JVM、多线程并发、spring原理、微服务、Netty 与RPC 、Kafka、日记、设计模式、Java算法、数据库、Zookeeper、分布式缓存、数据结构等等。

2020最新的Spring Boot 分布式锁的具体实现(内附代码)的更多相关文章

  1. spring boot 分布式锁组件 spring-boot-klock-starter

    基于redis的分布式锁spring-boot starter组件,使得项目拥有分布式锁能力变得异常简单,支持spring boot,和spirng mvc等spring相关项目 快速开始 sprin ...

  2. Spring Boot 乐观锁加锁失败 - 使用AOP恢复错误

    之前写了一些辅助工作相关的Spring Boot怎么使用AOP.这里继续正题,怎么减少Spring Boot 乐观锁加锁报错的情况(基本可以解决). 1. 包依赖 spring-boot-starte ...

  3. spring boot分布式技术,spring cloud,负载均衡,配置管理器

    spring boot分布式的实现,使用spring cloud技术. 下边是我理解的spring cloud的核心技术: 1.配置服务器 2.注册发现服务器eureka(spring boot默认使 ...

  4. spring boot 分布式session实现

    spring boot 分布式session实现 主要是通过包装HttpServletRequest将session相关的方法进行代理. 具体是的实现就是通过SessionRepositoryFilt ...

  5. Spring Boot 乐观锁加锁失败 - 集成AOP

    Spring Boot with AOP 手头上的项目使用了Spring Boot, 在高并发的情况下,经常出现乐观锁加锁失败的情况(OptimisticLockingFailureException ...

  6. 2019阿里P7最新总结Spring Boot面试问题

        Spring Boot一直是Spring生态系统的关键参与者.该项目通过其自动配置功能使我们的生活更加轻松.在本教程中,我们将介绍在求职面试中可能出现的一些与Spring Boot相关的最常见 ...

  7. Redis整合Spring实现分布式锁

    spring把专门的数据操作独立封装在spring-data系列中,spring-data-redis是对Redis的封装 <dependencies> <!-- 添加spring- ...

  8. spring boot 分布式事务实现(XA方式)

    关于spring boot 支持分布式事务,XA是常用的一种方式. 这里把相关的配置记下,方便以后使用. 首先配置两个不同的数据源 : 订单库.持仓库. /** * Created by zhangj ...

  9. 10、Spring Boot分布式

    1.分布式简介  2.Zookeeper和Dubbo  3.zookeeper (1).zookeeper安装 官方文档:https://hub.docker.com/_/zookeeper?tab= ...

随机推荐

  1. 温故知新-java虚拟机

    文章目录 java虚拟机是什么? jvm的体系结构 第一个类加载子系统 类的生命周期 加载器分类 类加载机制 第二个运行时数据区(内存结构) GC算法和收集器 如何判断对象可以被回收? 如何判断一个常 ...

  2. k8s学习-文档&概念

    1.文档大全 kubernetes objects文档(yaml文件编写): https://kubernetes.io/docs/concepts/overview/working-with-obj ...

  3. chrome浏览器版本与驱动不匹配问题的解决办法

    1.浏览器与驱动如何匹配才不会报错 使用selenium模块的webdriver打开谷歌浏览器时常遇到这样的错误提示: selenium.common.exceptions.WebDriverExce ...

  4. MYSQL SQL 语句修改字段默认值

    alter table tablename alter column drop default; (若本身存在默认值,则先删除) alter table tablename alter column ...

  5. Python第三方库 - 安装

    目录 1. 代码格式化 1.1. autopep8 1.2. YAPF 1.3. docformatter 2. 视觉相关 2.1. pyzbar, 条码(二维码)识别 2.2. tesserocr ...

  6. 读Linux高性能服务器编程-12章http服务器源码

    title:用线程池实现的http服务器 从main函数看起 解析ip地址(点分制, 端口号) 设置忽略SIGPIPE信号 初始化线程池,池中创建了8个线程,每个线程对应一个work函数 初始化htt ...

  7. docker镜像瘦身思路

    docker镜像瘦身思路 一.简介 docker镜像太大,带来了以下几个问题: 存储开销 这块影响其实不算很大,因为对服务器磁盘来说,15GB的存储空间并不算大,除非用户服务器的磁盘空间很紧张 部署时 ...

  8. 深入理解JVM(③)虚拟机性能监控、故障处理工具

    前言 JDK的bin目录中有一系列的小工具,除了java.exe.javac.exe这两个编译和运行Java程序外,还有打包.部署.签名.调试.监控.运维等各种场景都会用到这些小工具. 这些工具根据软 ...

  9. Python 简明教程 --- 14,Python 数据结构进阶

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 如果你发现特殊情况太多,那很可能是用错算法了. -- Carig Zerouni 目录 前几节我们介 ...

  10. 一不小心,我就上传了 279674 字的 MySQL 学习资料到 github 上了

    自从2019年11月我们出版了<千金良方--MySQL 性能优化金字塔法则>一书之后,持续不断有人来询问我MySQL 4 个系统字典库相关的问题,因为篇幅原因,书中并没有完整收录4个字典库 ...