文档章节

2020最新的Spring Boot 分布式锁的具体实现(内附代码)

北柠Java
 北柠Java
发布于 07/07 19:40
字数 1285
阅读 41
收藏 0

行业解决方案、产品招募中!想赚钱就来传!>>>

前言

面试总是会被问到有没有用过分布式锁、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、分布式缓存、数据结构等等。

file

北柠Java
粉丝 0
博文 100
码字总数 150942
作品 0
长沙
程序员
私信 提问
加载中
请先登录后再评论。
Netty那点事(三)Channel与Pipeline

Channel是理解和使用Netty的核心。Channel的涉及内容较多,这里我使用由浅入深的介绍方法。在这篇文章中,我们主要介绍Channel部分中Pipeline实现机制。为了避免枯燥,借用一下《盗梦空间》的...

黄亿华
2013/11/24
2W
22
访问安全控制解决方案

本文是《轻量级 Java Web 框架架构设计》的系列博文。 今天想和大家简单的分享一下,在 Smart 中是如何做到访问安全控制的。也就是说,当没有登录或 Session 过期时所做的操作,会自动退回到...

黄勇
2013/11/03
3.4K
6
用vertx实现高吞吐量的站点计数器

工具:vertx,redis,mongodb,log4j 源代码地址:https://github.com/jianglibo/visitrank 先看架构图: 如果你不熟悉vertx,请先google一下。我这里将vertx当作一个容器,上面所有的圆圈要...

jianglibo
2014/04/03
3.9K
3
SQLServer实现split分割字符串到列

网上已有人实现sqlserver的split函数可将字符串分割成行,但是我们习惯了split返回数组或者列表,因此这里对其做一些改动,最终实现也许不尽如意,但是也能解决一些问题。 先贴上某大牛写的s...

cwalet
2014/05/21
9.6K
0
我的架构演化笔记 功能1: 基本的用户注册

“咚咚”,一阵急促的敲门声, 我从睡梦中惊醒,我靠,这才几点,谁这么早, 开门一看,原来我的小表弟放暑假了,来南京玩,顺便说跟我后面学习一个网站是怎么做出来的。 于是有了下面的一段...

强子哥哥
2014/05/31
976
3

没有更多内容

加载失败,请刷新页面

加载更多

好用到爆的 Java 技巧

本文不是一个吹嘘的文章,不会讲很多高深的架构,相反,会讲解很多基础的问题和写法问题,如果读者自认为基础问题和写法问题都是不是问题,那请忽略这篇文章,节省出时间去做一些有意义的事情...

码农突围
34分钟前
8
0
消息队列(MessageQueue)-分析

这里分析消息队列的原理和一般做法和其理念价值 这里还会 分析 NATS 和其可改进点 TODO

梦想游戏人
38分钟前
20
0
Redis 教程

Redis 教程 REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。 Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的...

rootliu
40分钟前
9
0
SPSSAU 付费数据研究报告服务

SPSSAU-付费数据分析报告服务(周老师提供) 本文分享自微信公众号 - SPSSAU(spssau)。 如有侵权,请联系 support@oschina.cn 删除。 本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起...

SPSSAU
2017/11/08
0
0
芋艿-springcloud gateway

http://www.iocoder.cn/Spring-Cloud/Spring-Cloud-Gateway/?github springcloud gateway 官方文档 https://cloud.spring.io/spring-cloud-gateway/reference/html/#gatewayfilter-factories......

Java搬砖工程师
59分钟前
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部