SpringBoot 分布式锁的实现

在这里插入图片描述
来源:https://urlify.cn/632yIv


前言

最近面试总是会被问到有没有用过分布式锁、redis锁,由于平时很少接触到,所以只能很无奈的回答“没有”。回来之后就恶补了一下,本文主要做下记录,通过SpringBoot整合redisson来实现分布式锁,并结合demo测试结果。

首先看下大佬总结的图

来源: https://www.cnblogs.com/qdhxhz/p/11046905.html

正文

增加依赖


 
 
  1. <!--redis-->
  2. <dependency>
  3. <groupId>org.springframework.boot </groupId>
  4. <artifactId>spring-boot-starter-data-redis </artifactId>
  5. </dependency>
  6. <!--redisson-->
  7. <dependency>
  8. <groupId>org.redisson </groupId>
  9. <artifactId>redisson-spring-boot-starter </artifactId>
  10. <version>3.10.6 </version>
  11. </dependency>

配置 信息


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

配置类


 
 
  1. /**
  2. * redisson 配置,下面是单节点配置:
  3. *
  4. * @author gourd
  5. */
  6. @Configuration
  7. public class RedissonConfig {
  8. @Value("${spring.redis.host}")
  9. private String host;
  10. @Value("${spring.redis.port}")
  11. private String port;
  12. @Value("${spring.redis.password:}")
  13. private String password;
  14. @Bean
  15. public RedissonClient redissonClient() {
  16. Config config = new Config();
  17. //单节点
  18. config.useSingleServer().setAddress( "redis://" + host + ":" + port);
  19. if (StringUtils.isEmpty(password)) {
  20. config.useSingleServer().setPassword( null);
  21. } else {
  22. config.useSingleServer().setPassword(password);
  23. }
  24. //添加主从配置
  25. // config.useMasterSlaveServers().setMasterAddress("").setPassword("").addSlaveAddress(new String[]{"",""});
  26. // 集群模式配置 setScanInterval()扫描间隔时间,单位是毫秒, //可以用"rediss://"来启用SSL连接
  27. // config.useClusterServers().setScanInterval(2000).addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001").addNodeAddress("redis://127.0.0.1:7002");
  28. return Redisson.create(config);
  29. }
  30. }

Redisson工具类


 
 
  1. /**
  2. * redis分布式锁帮助类
  3. *
  4. * @author gourd
  5. *
  6. */
  7. public class RedisLockUtil {
  8. private static DistributedLocker distributedLocker = SpringContextHolder.getBean( "distributedLocker",DistributedLocker.class);
  9. /**
  10. * 加锁
  11. * @param lockKey
  12. * @return
  13. */
  14. public static RLock lock(String lockKey) {
  15. return distributedLocker.lock(lockKey);
  16. }
  17. /**
  18. * 释放锁
  19. * @param lockKey
  20. */
  21. public static void unlock(String lockKey) {
  22. distributedLocker.unlock(lockKey);
  23. }
  24. /**
  25. * 释放锁
  26. * @param lock
  27. */
  28. public static void unlock(RLock lock) {
  29. distributedLocker.unlock(lock);
  30. }
  31. /**
  32. * 带超时的锁
  33. * @param lockKey
  34. * @param timeout 超时时间 单位:秒
  35. */
  36. public static RLock lock(String lockKey, int timeout) {
  37. return distributedLocker.lock(lockKey, timeout);
  38. }
  39. /**
  40. * 带超时的锁
  41. * @param lockKey
  42. * @param unit 时间单位
  43. * @param timeout 超时时间
  44. */
  45. public static RLock lock(String lockKey, int timeout,TimeUnit unit ) {
  46. return distributedLocker.lock(lockKey, unit, timeout);
  47. }
  48. /**
  49. * 尝试获取锁
  50. * @param lockKey
  51. * @param waitTime 最多等待时间
  52. * @param leaseTime 上锁后自动释放锁时间
  53. * @return
  54. */
  55. public static boolean tryLock(String lockKey, int waitTime, int leaseTime) {
  56. return distributedLocker.tryLock(lockKey, TimeUnit.SECONDS, waitTime, leaseTime);
  57. }
  58. /**
  59. * 尝试获取锁
  60. * @param lockKey
  61. * @param unit 时间单位
  62. * @param waitTime 最多等待时间
  63. * @param leaseTime 上锁后自动释放锁时间
  64. * @return
  65. */
  66. public static boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
  67. return distributedLocker.tryLock(lockKey, unit, waitTime, leaseTime);
  68. }
  69. /**
  70. * 获取计数器
  71. *
  72. * @param name
  73. * @return
  74. */
  75. public static RCountDownLatch getCountDownLatch(String name){
  76. return distributedLocker.getCountDownLatch(name);
  77. }
  78. /**
  79. * 获取信号量
  80. *
  81. * @param name
  82. * @return
  83. */
  84. public static RSemaphore getSemaphore(String name){
  85. return distributedLocker.getSemaphore(name);
  86. }
  87. }

底层封装


 
 
  1. /**
  2. * @author gourd
  3. */
  4. public interface DistributedLocker {
  5. RLock lock(String lockKey);
  6. RLock lock(String lockKey, int timeout);
  7. RLock lock(String lockKey, TimeUnit unit, int timeout);
  8. boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime);
  9. void unlock(String lockKey);
  10. void unlock(RLock lock);
  11. }
  12. /**
  13. * @author gourd
  14. */
  15. @Component
  16. public class RedisDistributedLocker implements DistributedLocker {
  17. @Autowired
  18. private RedissonClient redissonClient;
  19. @ Override
  20. public RLock lock( String lockKey) {
  21. RLock lock = redissonClient.getLock(lockKey);
  22. lock. lock();
  23. return lock;
  24. }
  25. @ Override
  26. public RLock lock( String lockKey, int leaseTime) {
  27. RLock lock = redissonClient.getLock(lockKey);
  28. lock. lock(leaseTime, TimeUnit.SECONDS);
  29. return lock;
  30. }
  31. @ Override
  32. public RLock lock( String lockKey, TimeUnit unit ,int timeout) {
  33. RLock lock = redissonClient.getLock(lockKey);
  34. lock. lock(timeout, unit);
  35. return lock;
  36. }
  37. @ Override
  38. public boolean tryLock( String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
  39. RLock lock = redissonClient.getLock(lockKey);
  40. try {
  41. return lock.tryLock(waitTime, leaseTime, unit);
  42. } catch (InterruptedException e) {
  43. return false;
  44. }
  45. }
  46. @ Override
  47. public void unlock( String lockKey) {
  48. RLock lock = redissonClient.getLock(lockKey);
  49. lock.unlock();
  50. }
  51. @ Override
  52. public void unlock( RLock lock) {
  53. lock.unlock();
  54. }
  55. }

测试

模拟并发测试


 
 
  1. /**
  2. * redis分布式锁控制器
  3. * @author gourd
  4. * @since 2019-07-30
  5. */
  6. @RestController
  7. @Api(tags = "redisson", description = "redis分布式锁控制器" )
  8. @RequestMapping( "/redisson" )
  9. @Slf4j
  10. public class RedissonLockController {
  11. /**
  12. * 锁测试共享变量
  13. */
  14. private Integer lockCount = 10;
  15. /**
  16. * 无锁测试共享变量
  17. */
  18. private Integer count = 10;
  19. /**
  20. * 模拟线程数
  21. */
  22. private static int threadNum = 10;
  23. /**
  24. * 模拟并发测试加锁和不加锁
  25. * @return
  26. */
  27. @GetMapping( "/test")
  28. @ApiOperation(value = "模拟并发测试加锁和不加锁")
  29. public void lock(){
  30. // 计数器
  31. final CountDownLatch countDownLatch = new CountDownLatch( 1);
  32. for ( int i = 0; i < threadNum; i ++) {
  33. MyRunnable myRunnable = new MyRunnable(countDownLatch);
  34. Thread myThread = new Thread(myRunnable);
  35. myThread.start();
  36. }
  37. // 释放所有线程
  38. countDownLatch.countDown();
  39. }
  40. /**
  41. * 加锁测试
  42. */
  43. private void testLockCount() {
  44. String lockKey = "lock-test";
  45. try {
  46. // 加锁,设置超时时间2s
  47. RedisLockUtil.lock(lockKey, 2, TimeUnit.SECONDS);
  48. lockCount--;
  49. log.info( "lockCount值:"+lockCount);
  50. } catch (Exception e){
  51. log.error(e.getMessage(),e);
  52. } finally {
  53. // 释放锁
  54. RedisLockUtil.unlock(lockKey);
  55. }
  56. }
  57. /**
  58. * 无锁测试
  59. */
  60. private void testCount() {
  61. count--;
  62. log.info( "count值:"+count);
  63. }
  64. public class MyRunnable implements Runnable {
  65. /**
  66. * 计数器
  67. */
  68. final CountDownLatch countDownLatch;
  69. public MyRunnable(CountDownLatch countDownLatch) {
  70. this.countDownLatch = countDownLatch;
  71. }
  72. @Override
  73. public void run() {
  74. try {
  75. // 阻塞当前线程,直到计时器的值为0
  76. countDownLatch.await();
  77. } catch (InterruptedException e) {
  78. log.error(e.getMessage(),e);
  79. }
  80. // 无锁操作
  81. testCount();
  82. // 加锁操作
  83. testLockCount();
  84. }
  85. }
  86. }

调用接口后打印值:

测试结果

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

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

====================================================

代码均已上传至本人的开源项目

spring-cloud-plus:https://blog.csdn.net/HXNLYW/article/details/104635673

 


-END-

java学长

专注于Java干货分享

在这里插入图片描述

扫描上方二维码获取更多Java干货

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值