前言

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

首先看下大佬总结的图

正文

添加依赖

 
  1. <!--redis-->

  2. <dependency>

    1. <groupId>org.springframework.boot</groupId>

    2. <artifactId>spring-boot-starter-data-redis</artifactId>

  3. </dependency>

  4. <!--redisson-->

  5. <dependency>

    1. <groupId>org.redisson</groupId>

    2. <artifactId>redisson-spring-boot-starter</artifactId>

    3. <version>3.10.6</version>

  6. </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. publicclassRedissonConfig{

  8. @Value("${spring.redis.host}")

  9. privateString host;

  10. @Value("${spring.redis.port}")

  11. privateString port;

  12. @Value("${spring.redis.password:}")

  13. privateString password;

  14.  

  15. @Bean

  16. publicRedissonClient redissonClient() {

  17. Config config = newConfig();

  18. //单节点

  19. config.useSingleServer().setAddress("redis://"+ host + ":"+ port);

  20. if(StringUtils.isEmpty(password)) {

  21. config.useSingleServer().setPassword(null);

  22. } else{

  23. config.useSingleServer().setPassword(password);

  24. }

  25. //添加主从配置

  26. // config.useMasterSlaveServers().setMasterAddress("").setPassword("").addSlaveAddress(new String[]{"",""});

  27. // 集群模式配置 setScanInterval()扫描间隔时间,单位是毫秒, //可以用"rediss://"来启用SSL连接

  28. // config.useClusterServers().setScanInterval(2000).addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001").addNodeAddress("redis://127.0.0.1:7002");

  29. returnRedisson.create(config);

  30. }

  31. }

 

Redisson 工具类

 
  1. /**

  2. * redis分布式锁帮助类

  3. *

  4. * @author gourd

  5. *

  6. */

  7. publicclassRedisLockUtil{

  8.  

  9. privatestaticDistributedLocker distributedLocker = SpringContextHolder.getBean("distributedLocker",DistributedLocker.class);

  10.  

  11. /**

  12. * 加锁

  13. * @param lockKey

  14. * @return

  15. */

  16. publicstaticRLocklock(String lockKey) {

  17. return distributedLocker.lock(lockKey);

  18. }

  19.  

  20. /**

  21. * 释放锁

  22. * @param lockKey

  23. */

  24. publicstaticvoid unlock(String lockKey) {

  25. distributedLocker.unlock(lockKey);

  26. }

  27.  

  28. /**

  29. * 释放锁

  30. * @param lock

  31. */

  32. publicstaticvoid unlock(RLocklock) {

  33. distributedLocker.unlock(lock);

  34. }

  35.  

  36. /**

  37. * 带超时的锁

  38. * @param lockKey

  39. * @param timeout 超时时间 单位:秒

  40. */

  41. publicstaticRLocklock(String lockKey, int timeout) {

  42. return distributedLocker.lock(lockKey, timeout);

  43. }

  44.  

  45. /**

  46. * 带超时的锁

  47. * @param lockKey

  48. * @param unit 时间单位

  49. * @param timeout 超时时间

  50. */

  51. publicstaticRLocklock(String lockKey, int timeout,TimeUnit unit ) {

  52. return distributedLocker.lock(lockKey, unit, timeout);

  53. }

  54.  

  55. /**

  56. * 尝试获取锁

  57. * @param lockKey

  58. * @param waitTime 最多等待时间

  59. * @param leaseTime 上锁后自动释放锁时间

  60. * @return

  61. */

  62. publicstaticboolean tryLock(String lockKey, int waitTime, int leaseTime) {

  63. return distributedLocker.tryLock(lockKey, TimeUnit.SECONDS, waitTime, leaseTime);

  64. }

  65.  

  66. /**

  67. * 尝试获取锁

  68. * @param lockKey

  69. * @param unit 时间单位

  70. * @param waitTime 最多等待时间

  71. * @param leaseTime 上锁后自动释放锁时间

  72. * @return

  73. */

  74. publicstaticboolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {

  75. return distributedLocker.tryLock(lockKey, unit, waitTime, leaseTime);

  76. }

  77.  

  78. /**

  79. * 获取计数器

  80. *

  81. * @param name

  82. * @return

  83. */

  84. publicstaticRCountDownLatch getCountDownLatch(String name){

  85. return distributedLocker.getCountDownLatch(name);

  86. }

  87.  

  88. /**

  89. * 获取信号量

  90. *

  91. * @param name

  92. * @return

  93. */

  94. publicstaticRSemaphore getSemaphore(String name){

  95. return distributedLocker.getSemaphore(name);

  96. }

  97. }

 

底层封装

 
  1. /**

  2. * @author gourd

  3. */

  4. publicinterfaceDistributedLocker{

  5.  

  6. RLocklock(String lockKey);

  7.  

  8. RLocklock(String lockKey, int timeout);

  9.  

  10. RLocklock(String lockKey, TimeUnit unit, int timeout);

  11.  

  12. boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime);

  13.  

  14. void unlock(String lockKey);

  15.  

  16. void unlock(RLocklock);

  17. }

  18.  

  19. /**

  20. * @author gourd

  21. */

  22. @Component

  23. publicclassRedisDistributedLockerimplementsDistributedLocker{

  24.  

  25. @Autowired

  26. privateRedissonClient redissonClient;

  27.  

  28. @Override

  29. publicRLocklock(String lockKey) {

  30. RLocklock= redissonClient.getLock(lockKey);

  31. lock.lock();

  32. returnlock;

  33. }

  34.  

  35. @Override

  36. publicRLocklock(String lockKey, int leaseTime) {

  37. RLocklock= redissonClient.getLock(lockKey);

  38. lock.lock(leaseTime, TimeUnit.SECONDS);

  39. returnlock;

  40. }

  41.  

  42. @Override

  43. publicRLocklock(String lockKey, TimeUnit unit ,int timeout) {

  44. RLocklock= redissonClient.getLock(lockKey);

  45. lock.lock(timeout, unit);

  46. returnlock;

  47. }

  48.  

  49. @Override

  50. publicboolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {

  51. RLocklock= redissonClient.getLock(lockKey);

  52. try{

  53. returnlock.tryLock(waitTime, leaseTime, unit);

  54. } catch(InterruptedException e) {

  55. returnfalse;

  56. }

  57. }

  58.  

  59. @Override

  60. publicvoid unlock(String lockKey) {

  61. RLocklock= redissonClient.getLock(lockKey);

  62. lock.unlock();

  63. }

  64.  

  65. @Override

  66. publicvoid unlock(RLocklock) {

  67. lock.unlock();

  68. }

  69. }

 

测试

模拟并发测试

 
  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. publicclassRedissonLockController{

  11.  

  12. /**

  13. * 锁测试共享变量

  14. */

  15. privateInteger lockCount = 10;

  16.  

  17. /**

  18. * 无锁测试共享变量

  19. */

  20. privateInteger count = 10;

  21.  

  22. /**

  23. * 模拟线程数

  24. */

  25. privatestaticint threadNum = 10;

  26.  

  27. /**

  28. * 模拟并发测试加锁和不加锁

  29. * @return

  30. */

  31. @GetMapping("/test")

  32. @ApiOperation(value = "模拟并发测试加锁和不加锁")

  33. publicvoidlock(){

  34. // 计数器

  35. finalCountDownLatch countDownLatch = newCountDownLatch(1);

  36. for(int i = 0; i < threadNum; i ++) {

  37. MyRunnable myRunnable = newMyRunnable(countDownLatch);

  38. Thread myThread = newThread(myRunnable);

  39. myThread.start();

  40. }

  41. // 释放所有线程

  42. countDownLatch.countDown();

  43. }

  44.  

  45. /**

  46. * 加锁测试

  47. */

  48. privatevoid testLockCount() {

  49. String lockKey = "lock-test";

  50. try{

  51. // 加锁,设置超时时间2s

  52. RedisLockUtil.lock(lockKey,2, TimeUnit.SECONDS);

  53. lockCount--;

  54. log.info("lockCount值:"+lockCount);

  55. }catch(Exception e){

  56. log.error(e.getMessage(),e);

  57. }finally{

  58. // 释放锁

  59. RedisLockUtil.unlock(lockKey);

  60. }

  61. }

  62.  

  63. /**

  64. * 无锁测试

  65. */

  66. privatevoid testCount() {

  67. count--;

  68. log.info("count值:"+count);

  69. }

  70.  

  71.  

  72. publicclassMyRunnableimplementsRunnable{

  73. /**

  74. * 计数器

  75. */

  76. finalCountDownLatch countDownLatch;

  77.  

  78. publicMyRunnable(CountDownLatch countDownLatch) {

  79. this.countDownLatch = countDownLatch;

  80. }

  81.  

  82. @Override

  83. publicvoid run() {

  84. try{

  85. // 阻塞当前线程,直到计时器的值为0

  86. countDownLatch.await();

  87. } catch(InterruptedException e) {

  88. log.error(e.getMessage(),e);

  89. }

  90. // 无锁操作

  91. testCount();

  92. // 加锁操作

  93. testLockCount();

  94. }

  95.  

  96. }

  97.  

  98. }

 

调用接口后打印值:

测试结果

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

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