前言
面试总是会被问到有没有用过分布式锁、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 -
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 工具类
-
/** -
* 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); -
} -
}
底层封装
-
/** -
* @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(); -
} -
}
测试
模拟并发测试
-
/** -
* 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-- 后值是乱序的,而加锁后的结果和我们预期的一样。
由于条件问题没办法测试分布式的并发。只能模拟单服务的这种并发,但是原理是一样,希望对大家有帮助。如有错误之处,欢迎指正。



京公网安备 11010502036488号