转码, 原文地址 https://mp.weixin.qq.com/s/WznBj2-zEtVBIbUqA5NQUQ
来源:https://urlify.cn/632yIv
前言
面试总是会被问到有没有用过分布式锁、redis 锁,大部分读者平时很少接触到,所以只能很无奈的回答 “没有”。本文通过 Spring Boot 整合 redisson 来实现分布式锁,并结合 demo 测试结果。
首先看下大佬总结的图
正文
添加依赖
1 2 3 4 5 6 7 8 9 10 11
| <!--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>
|
配置信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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
|
配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| /** * redisson 配置,下面是单节点配置: * * @author gourd */ @Configuration publicclassRedissonConfig{ @Value("${spring.redis.host}") privateString host; @Value("${spring.redis.port}") privateString port; @Value("${spring.redis.password:}") privateString password; @Bean publicRedissonClient redissonClient() { Config config = newConfig(); //单节点 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"); returnRedisson.create(config); } }
|
Redisson 工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| /** * redis分布式锁帮助类 * * @author gourd * */ publicclassRedisLockUtil{ privatestaticDistributedLocker distributedLocker = SpringContextHolder.getBean("distributedLocker",DistributedLocker.class); /** * 加锁 * @param lockKey * @return */ publicstaticRLocklock(String lockKey) { return distributedLocker.lock(lockKey); } /** * 释放锁 * @param lockKey */ publicstaticvoid unlock(String lockKey) { distributedLocker.unlock(lockKey); } /** * 释放锁 * @param lock */ publicstaticvoid unlock(RLocklock) { distributedLocker.unlock(lock); } /** * 带超时的锁 * @param lockKey * @param timeout 超时时间 单位:秒 */ publicstaticRLocklock(String lockKey, int timeout) { return distributedLocker.lock(lockKey, timeout); } /** * 带超时的锁 * @param lockKey * @param unit 时间单位 * @param timeout 超时时间 */ publicstaticRLocklock(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 */ publicstaticRCountDownLatch getCountDownLatch(String name){ return distributedLocker.getCountDownLatch(name); } /** * 获取信号量 * * @param name * @return */ publicstaticRSemaphore getSemaphore(String name){ return distributedLocker.getSemaphore(name); } }
|
底层封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| /** * @author gourd */ publicinterfaceDistributedLocker{ RLocklock(String lockKey); RLocklock(String lockKey, int timeout); RLocklock(String lockKey, TimeUnit unit, int timeout); boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime); void unlock(String lockKey); void unlock(RLocklock); } /** * @author gourd */ @Component publicclassRedisDistributedLockerimplementsDistributedLocker{ @Autowired privateRedissonClient redissonClient; @Override publicRLocklock(String lockKey) { RLocklock= redissonClient.getLock(lockKey); lock.lock(); returnlock; } @Override publicRLocklock(String lockKey, int leaseTime) { RLocklock= redissonClient.getLock(lockKey); lock.lock(leaseTime, TimeUnit.SECONDS); returnlock; } @Override publicRLocklock(String lockKey, TimeUnit unit ,int timeout) { RLocklock= redissonClient.getLock(lockKey); lock.lock(timeout, unit); returnlock; } @Override publicboolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) { RLocklock= redissonClient.getLock(lockKey); try{ returnlock.tryLock(waitTime, leaseTime, unit); } catch(InterruptedException e) { returnfalse; } } @Override publicvoid unlock(String lockKey) { RLocklock= redissonClient.getLock(lockKey); lock.unlock(); } @Override publicvoid unlock(RLocklock) { lock.unlock(); } }
|
测试
模拟并发测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| /** * redis分布式锁控制器 * @author gourd * @since 2019-07-30 */ @RestController @Api(tags = "redisson", description = "redis分布式锁控制器") @RequestMapping("/redisson") @Slf4j publicclassRedissonLockController{ /** * 锁测试共享变量 */ privateInteger lockCount = 10; /** * 无锁测试共享变量 */ privateInteger count = 10; /** * 模拟线程数 */ privatestaticint threadNum = 10; /** * 模拟并发测试加锁和不加锁 * @return */ @GetMapping("/test") @ApiOperation(value = "模拟并发测试加锁和不加锁") publicvoidlock(){ // 计数器 finalCountDownLatch countDownLatch = newCountDownLatch(1); for(int i = 0; i < threadNum; i ++) { MyRunnable myRunnable = newMyRunnable(countDownLatch); Thread myThread = newThread(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); } publicclassMyRunnableimplementsRunnable{ /** * 计数器 */ finalCountDownLatch countDownLatch; publicMyRunnable(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } @Override publicvoid run() { try{ // 阻塞当前线程,直到计时器的值为0 countDownLatch.await(); } catch(InterruptedException e) { log.error(e.getMessage(),e); } // 无锁操作 testCount(); // 加锁操作 testLockCount(); } } }
|
调用接口后打印值:
测试结果
根据打印结果可以明显看到,未加锁的 count– 后值是乱序的,而加锁后的结果和我们预期的一样。
由于条件问题没办法测试分布式的并发。只能模拟单服务的这种并发,但是原理是一样,希望对大家有帮助。如有错误之处,欢迎指正。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 如有文章对你有帮助,
“在看”和转发是对我最大的支持!
关注Java开发宝典
每天学习Java技术
点赞是最大的支持
|