Spring Boot 分布式锁的实现

点击上方 "程序员小乐"关注, 星标或置顶一起成长

每天凌晨00点00分, 第一时间与你相约

    

每日英文

Things in this world are temporary. If events are turning well, enjoy it. If they go wrong don’t worry, it won’t last long.

世界上的所有事情都是暂时的,如果事事顺心,那就好好享受;如果发生意外,不要过于担心,一切都会过去的。

每日掏心

秒针、分针、时针,拖着虚影转动成无数密密麻麻的日子,最终汇聚成时间的长河,变成我们所生活的庞大的时代。

来自:葫芦胡 | 责编:乐乐

链接:urlify.cn/632yIv

程序员小乐(ID:study_tech)第 1004 次推文

往日回顾:《最受欢迎的男友职业排行榜Top10》

     

   正文   

前言

面试总是会被问到有没有用过分布式锁、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);
}
}

底层封装

搜索公众号程序员小乐回复关键字“offer”,获取算法面试题和答案。

/**
 * @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();
}
}

测试

模拟并发测试

搜索公众号程序员小乐回复关键字“Java”,获取Java面试题和答案。

/**
 * 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-- 后值是乱序的,而加锁后的结果和我们预期的一样。

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

欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,欢迎转发分享给更多人。欢迎加入程序员小乐技术交流群,在后台回复“加群”或者“学习”即可。

猜你还想看

阿里、腾讯、百度、华为、京东最新面试题汇集

超实用超牛X!Linux系统内存知识总结!

推荐 33 个 IDEA 最牛配置,写代码太爽了!

IDEA 上位?不!Eclipse Theia 1.0 发布!

关注订阅号「程序员小乐」,收看更多精彩内容

嘿,你在看吗

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值