前言

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

首先看下大佬总结的图

正文

添加依赖

  1. <!--redis-->
  2. <dependency>
  3. <groupId>
  4. org.springframework.boot
  5. </groupId>
  6. <artifactId>
  7. spring-boot-starter-data-redis
  8. </artifactId>
  9. </dependency>
  10. <!--redisson-->
  11. <dependency>
  12. <groupId>
  13. org.redisson
  14. </groupId>
  15. <artifactId>
  16. redisson-spring-boot-starter
  17. </artifactId>
  18. <version>
  19. 3.10.6
  20. </version>
  21. </dependency>

配置信息

  1. spring:
  2. # redis
  3. redis:
  4. host:
  5. 47.103
  6. .
  7. 5.190
  8. port:
  9. 6379
  10. jedis:
  11. pool:
  12. # 连接池最大连接数(使用负值表示没有限制)
  13. max-active: 100
  14. # 连接池中的最小空闲连接
  15. max-idle: 10
  16. # 连接池最大阻塞等待时间(使用负值表示没有限制)
  17. max-wait: -1
  18. # 连接超时时间(毫秒)
  19. timeout: 5000
  20. #默认是索引为0的数据库
  21. database: 0

配置类

  1. /**
  2. * redisson 配置,下面是单节点配置:
  3. *
  4. * @author gourd
  5. */
  6. @Configuration
  7. publicclass
  8. RedissonConfig
  9. {
  10. @Value
  11. (
  12. "${spring.redis.host}"
  13. )
  14. private
  15. String
  16. host;
  17. @Value
  18. (
  19. "${spring.redis.port}"
  20. )
  21. private
  22. String
  23. port;
  24. @Value
  25. (
  26. "${spring.redis.password:}"
  27. )
  28. private
  29. String
  30. password;
  31. @Bean
  32. public
  33. RedissonClient
  34. redissonClient() {
  35. Config
  36. config =
  37. new
  38. Config
  39. ();
  40. //单节点
  41. config.useSingleServer().setAddress(
  42. "redis://"
  43. + host +
  44. ":"
  45. + port);
  46. if
  47. (
  48. StringUtils
  49. .isEmpty(password)) {
  50. config.useSingleServer().setPassword(
  51. null
  52. );
  53. }
  54. else
  55. {
  56. config.useSingleServer().setPassword(password);
  57. }
  58. //添加主从配置
  59. // config.useMasterSlaveServers().setMasterAddress("").setPassword("").addSlaveAddress(new String[]{"",""});
  60. // 集群模式配置 setScanInterval()扫描间隔时间,单位是毫秒, //可以用"rediss://"来启用SSL连接
  61. // config.useClusterServers().setScanInterval(2000).addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001").addNodeAddress("redis://127.0.0.1:7002");
  62. return
  63. Redisson
  64. .create(config);
  65. }
  66. }

Redisson 工具类

  1. /**
  2. * redis分布式锁帮助类
  3. *
  4. * @author gourd
  5. *
  6. */
  7. publicclass
  8. RedisLockUtil
  9. {
  10. privatestatic
  11. DistributedLocker
  12. distributedLocker =
  13. SpringContextHolder
  14. .getBean(
  15. "distributedLocker"
  16. ,
  17. DistributedLocker
  18. .
  19. class
  20. );
  21. /**
  22. * 加锁
  23. * @param lockKey
  24. * @return
  25. */
  26. publicstatic
  27. RLock
  28. lock
  29. (
  30. String
  31. lockKey) {
  32. return
  33. distributedLocker.
  34. lock
  35. (lockKey);
  36. }
  37. /**
  38. * 释放锁
  39. * @param lockKey
  40. */
  41. publicstaticvoid
  42. unlock(
  43. String
  44. lockKey) {
  45. distributedLocker.unlock(lockKey);
  46. }
  47. /**
  48. * 释放锁
  49. * @param lock
  50. */
  51. publicstaticvoid
  52. unlock(
  53. RLock
  54. lock
  55. ) {
  56. distributedLocker.unlock(
  57. lock
  58. );
  59. }
  60. /**
  61. * 带超时的锁
  62. * @param lockKey
  63. * @param timeout 超时时间 单位:秒
  64. */
  65. publicstatic
  66. RLock
  67. lock
  68. (
  69. String
  70. lockKey,
  71. int
  72. timeout) {
  73. return
  74. distributedLocker.
  75. lock
  76. (lockKey, timeout);
  77. }
  78. /**
  79. * 带超时的锁
  80. * @param lockKey
  81. * @param unit 时间单位
  82. * @param timeout 超时时间
  83. */
  84. publicstatic
  85. RLock
  86. lock
  87. (
  88. String
  89. lockKey,
  90. int
  91. timeout,
  92. TimeUnit
  93. unit ) {
  94. return
  95. distributedLocker.
  96. lock
  97. (lockKey, unit, timeout);
  98. }
  99. /**
  100. * 尝试获取锁
  101. * @param lockKey
  102. * @param waitTime 最多等待时间
  103. * @param leaseTime 上锁后自动释放锁时间
  104. * @return
  105. */
  106. publicstaticboolean
  107. tryLock(
  108. String
  109. lockKey,
  110. int
  111. waitTime,
  112. int
  113. leaseTime) {
  114. return
  115. distributedLocker.tryLock(lockKey,
  116. TimeUnit
  117. .SECONDS, waitTime, leaseTime);
  118. }
  119. /**
  120. * 尝试获取锁
  121. * @param lockKey
  122. * @param unit 时间单位
  123. * @param waitTime 最多等待时间
  124. * @param leaseTime 上锁后自动释放锁时间
  125. * @return
  126. */
  127. publicstaticboolean
  128. tryLock(
  129. String
  130. lockKey,
  131. TimeUnit
  132. unit,
  133. int
  134. waitTime,
  135. int
  136. leaseTime) {
  137. return
  138. distributedLocker.tryLock(lockKey, unit, waitTime, leaseTime);
  139. }
  140. /**
  141. * 获取计数器
  142. *
  143. * @param name
  144. * @return
  145. */
  146. publicstatic
  147. RCountDownLatch
  148. getCountDownLatch(
  149. String
  150. name){
  151. return
  152. distributedLocker.getCountDownLatch(name);
  153. }
  154. /**
  155. * 获取信号量
  156. *
  157. * @param name
  158. * @return
  159. */
  160. publicstatic
  161. RSemaphore
  162. getSemaphore(
  163. String
  164. name){
  165. return
  166. distributedLocker.getSemaphore(name);
  167. }
  168. }

底层封装

  1. /**
  2. * @author gourd
  3. */
  4. publicinterface
  5. DistributedLocker
  6. {
  7. RLock
  8. lock
  9. (
  10. String
  11. lockKey);
  12. RLock
  13. lock
  14. (
  15. String
  16. lockKey,
  17. int
  18. timeout);
  19. RLock
  20. lock
  21. (
  22. String
  23. lockKey,
  24. TimeUnit
  25. unit,
  26. int
  27. timeout);
  28. boolean
  29. tryLock(
  30. String
  31. lockKey,
  32. TimeUnit
  33. unit,
  34. int
  35. waitTime,
  36. int
  37. leaseTime);
  38. void
  39. unlock(
  40. String
  41. lockKey);
  42. void
  43. unlock(
  44. RLock
  45. lock
  46. );
  47. }
  48. /**
  49. * @author gourd
  50. */
  51. @Component
  52. publicclass
  53. RedisDistributedLocker
  54. implements
  55. DistributedLocker
  56. {
  57. @Autowired
  58. private
  59. RedissonClient
  60. redissonClient;
  61. @Override
  62. public
  63. RLock
  64. lock
  65. (
  66. String
  67. lockKey) {
  68. RLock
  69. lock
  70. = redissonClient.getLock(lockKey);
  71. lock
  72. .
  73. lock
  74. ();
  75. returnlock
  76. ;
  77. }
  78. @Override
  79. public
  80. RLock
  81. lock
  82. (
  83. String
  84. lockKey,
  85. int
  86. leaseTime) {
  87. RLock
  88. lock
  89. = redissonClient.getLock(lockKey);
  90. lock
  91. .
  92. lock
  93. (leaseTime,
  94. TimeUnit
  95. .SECONDS);
  96. returnlock
  97. ;
  98. }
  99. @Override
  100. public
  101. RLock
  102. lock
  103. (
  104. String
  105. lockKey,
  106. TimeUnit
  107. unit ,
  108. int
  109. timeout) {
  110. RLock
  111. lock
  112. = redissonClient.getLock(lockKey);
  113. lock
  114. .
  115. lock
  116. (timeout, unit);
  117. returnlock
  118. ;
  119. }
  120. @Override
  121. publicboolean
  122. tryLock(
  123. String
  124. lockKey,
  125. TimeUnit
  126. unit,
  127. int
  128. waitTime,
  129. int
  130. leaseTime) {
  131. RLock
  132. lock
  133. = redissonClient.getLock(lockKey);
  134. try
  135. {
  136. returnlock
  137. .tryLock(waitTime, leaseTime, unit);
  138. }
  139. catch
  140. (
  141. InterruptedException
  142. e) {
  143. returnfalse
  144. ;
  145. }
  146. }
  147. @Override
  148. publicvoid
  149. unlock(
  150. String
  151. lockKey) {
  152. RLock
  153. lock
  154. = redissonClient.getLock(lockKey);
  155. lock
  156. .unlock();
  157. }
  158. @Override
  159. publicvoid
  160. unlock(
  161. RLock
  162. lock
  163. ) {
  164. lock
  165. .unlock();
  166. }
  167. }

测试

模拟并发测试

  1. /**
  2. * redis分布式锁控制器
  3. * @author gourd
  4. * @since 2019-07-30
  5. */
  6. @RestController
  7. @Api
  8. (tags =
  9. "redisson"
  10. , description =
  11. "redis分布式锁控制器"
  12. )
  13. @RequestMapping
  14. (
  15. "/redisson"
  16. )
  17. @Slf4j
  18. publicclass
  19. RedissonLockController
  20. {
  21. /**
  22. * 锁测试共享变量
  23. */
  24. private
  25. Integer
  26. lockCount =
  27. 10
  28. ;
  29. /**
  30. * 无锁测试共享变量
  31. */
  32. private
  33. Integer
  34. count =
  35. 10
  36. ;
  37. /**
  38. * 模拟线程数
  39. */
  40. privatestaticint
  41. threadNum =
  42. 10
  43. ;
  44. /**
  45. * 模拟并发测试加锁和不加锁
  46. * @return
  47. */
  48. @GetMapping
  49. (
  50. "/test"
  51. )
  52. @ApiOperation
  53. (value =
  54. "模拟并发测试加锁和不加锁"
  55. )
  56. publicvoidlock
  57. (){
  58. // 计数器
  59. final
  60. CountDownLatch
  61. countDownLatch =
  62. new
  63. CountDownLatch
  64. (
  65. 1
  66. );
  67. for
  68. (
  69. int
  70. i =
  71. 0
  72. ; i < threadNum; i ++) {
  73. MyRunnable
  74. myRunnable =
  75. new
  76. MyRunnable
  77. (countDownLatch);
  78. Thread
  79. myThread =
  80. new
  81. Thread
  82. (myRunnable);
  83. myThread.start();
  84. }
  85. // 释放所有线程
  86. countDownLatch.countDown();
  87. }
  88. /**
  89. * 加锁测试
  90. */
  91. privatevoid
  92. testLockCount() {
  93. String
  94. lockKey =
  95. "lock-test"
  96. ;
  97. try
  98. {
  99. // 加锁,设置超时时间2s
  100. RedisLockUtil
  101. .
  102. lock
  103. (lockKey,
  104. 2
  105. ,
  106. TimeUnit
  107. .SECONDS);
  108. lockCount--;
  109. log.info(
  110. "lockCount值:"
  111. +lockCount);
  112. }
  113. catch
  114. (
  115. Exception
  116. e){
  117. log.error(e.getMessage(),e);
  118. }
  119. finally
  120. {
  121. // 释放锁
  122. RedisLockUtil
  123. .unlock(lockKey);
  124. }
  125. }
  126. /**
  127. * 无锁测试
  128. */
  129. privatevoid
  130. testCount() {
  131. count--;
  132. log.info(
  133. "count值:"
  134. +count);
  135. }
  136. publicclass
  137. MyRunnable
  138. implements
  139. Runnable
  140. {
  141. /**
  142. * 计数器
  143. */
  144. final
  145. CountDownLatch
  146. countDownLatch;
  147. public
  148. MyRunnable
  149. (
  150. CountDownLatch
  151. countDownLatch) {
  152. this
  153. .countDownLatch = countDownLatch;
  154. }
  155. @Override
  156. publicvoid
  157. run() {
  158. try
  159. {
  160. // 阻塞当前线程,直到计时器的值为0
  161. countDownLatch.
  162. await
  163. ();
  164. }
  165. catch
  166. (
  167. InterruptedException
  168. e) {
  169. log.error(e.getMessage(),e);
  170. }
  171. // 无锁操作
  172. testCount();
  173. // 加锁操作
  174. testLockCount();
  175. }
  176. }
  177. }

调用接口后打印值:

测试结果

根据打印结果可以明显看到,未加锁的 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. Action的三种实现方式,struts.xml配置的详细解释及其简单执行过程(二)

    勿以恶小而为之,勿以善小而不为--------------------------刘备 劝诸君,多行善事积福报,莫作恶 上一章简单介绍了Struts2的'两个蝴蝶飞,你好' (一),如果没有看过,请观 ...

  2. 循序渐进VUE+Element 前端应用开发(9)--- 界面语言国际化的处理

    我们开发的系统,一般可以不用考虑语言国际化的问题,大多数系统一般是给本国人使用的,而且直接使用中文开发界面会更加迅速 一些,不过框架最好能够支持国际化的处理,以便在需要的时候,可以花点时间来实现多语言 ...

  3. 一篇文章教会你用Python抓取抖音app热点数据

    今天给大家分享一篇简单的安卓app数据分析及抓取方法.以抖音为例,我们想要抓取抖音的热点榜数据. 要知道,这个数据是没有网页版的,只能从手机端下手. 首先我们要安装charles抓包APP数据,它是一 ...

  4. 如何在react中使用decorator

    2020-03-27 如何在react中使用decorator decorator目前都需要修改babel才能使用 说一下具体的操作方法 踩了一天的坑... 步骤1: yarn create reac ...

  5. c printf(“%d”,变量)函数

  6. 用python做时间序列预测九:ARIMA模型简介

    本篇介绍时间序列预测常用的ARIMA模型,通过了解本篇内容,将可以使用ARIMA预测一个时间序列. 什么是ARIMA? ARIMA是'Auto Regressive Integrated Moving ...

  7. Python实现二分法和黄金分割法

    运筹学课上,首先介绍了非线性规划算法中的无约束规划算法.二分法和黄金分割法是属于无约束规划算法的一维搜索法中的代表. 二分法:$$x_{1}^{(k+1)}=\frac{1}{2}(x_{R}^{(k ...

  8. Anroid组件滚动视图(ScollView)简单使用

    ScollView应用展示 在xml文件中添加滚动视图 activity_main.xml <?xml version="1.0" encoding="utf-8& ...

  9. ca13a_c++_顺序容器的操作6删除元素

    /*ca13a_c++_顺序容器的操作6删除元素c.erase(p) //删除迭代器p指向的位置c.erase(b,e) //删除b to e之间的数据,迭代器b包括,e不包括c.clear()//删 ...

  10. 学员操作——制作5s关闭广告

    <!DOCTYPE html><html> <head> <meta charset="utf-8"> <title>广 ...