Redis的分布式锁自我理解

Redis的分布式锁自我理解

redis分布式锁存在的意义:

在synchronized存在的条件下,synchronized是能解决单个服务的加锁操作,现在倡导微服务,不同的服务可能部署在不同的服务器上,因此,synchronized不能满足现在的开发需求,因此,redis的分布式锁诞生了

redis分布式锁的发展:

基于redis缓存中的setnx操作,可以将键值对缓存起来,缓存成功会返回1,若存在键,则返回0;因此,初步的redis分布式的锁由此诞生。

public static void firstLock() {
        //1.上锁
        Jedis redis = getJedis();
        Long lockResult = redis.setnx(LOCK_NAME, LOCK_VALUE);
        if (1 == lockResult) {
            // 2. 执行业务
            executeBusiness();
            // 3. 释放锁
            redis.del(LOCK_NAME);
        } else {
            // 获取锁失败
            System.out.println("Can not get lock");
        }
    }

上图所示,没有设置过期时间,一旦在释放资源之前的操作中存在宕机的情况,此键值对将永久存在于redis中,其他服务都会处于一直请求锁的状态;因此,给锁加缓存时间的情况出现;

@Service
public class ServerLock {
    @Resource
    private TollDao tollDao;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
/*
redis分布式锁:这种是阻塞时锁,在分布式项目中,多个进程访问同一个资源,再使用synchronized就不管用了,我们可以使用redis分布式锁,redis是单线程的。
    当两个进程对同一个资源操作时,就可以让两个进程设置相同的一个属性值,在一段时间,两个进程只能有一个进程能在redis中设置值
    当有两个进程A和B,进程A访问资源时,先去redis中去查看一个锁,这个锁就是一个key-value,如果存在,说明进程B已经运行了,进程A就会等待,并
        不断去尝试加锁,当B的锁到期,A就会到redis中加锁,然后A进程运行
*/
    public String selectById(Integer pid){
// 这就是加锁,就是进程在redis放入一个标识:    setIfAbsent():如果里边有相同的值,就不存入,返回0(false).相当于redis的setnx和expire两个方法
//      key\value字段就是存储的字段,timeout表示这个值多久过期
        Boolean status = stringRedisTemplate.opsForValue().setIfAbsent("product::" + pid, "fy", 3, TimeUnit.SECONDS);

        if(status){
            try{
            Toll toll = tollDao.selectById(pid);
            if(toll.getCount()>0){
                System.out.println("更改前的剩余"+toll.getCount());
                Thread.sleep(5000);
                toll.setCount(toll.getCount()-1);
                tollDao.updateById(toll);
                System.out.println("库存剩余:"+toll.getCount());
                return "库存减少成功";
            }else {
                return "库存不足";
            }
        }catch (Exception e){
                throw  new RuntimeException(e.getMessage());
        }finally {
       		 //释放锁资源 一定再finally
              stringRedisTemplate.delete("product::"+pid);
        }
        }else {
//            System.out.println("服务器正忙请稍后再试..........");
            return "服务器正忙请稍后再试..........";
        }

    }
}

但是,又延伸出来了另一个问题,在设置缓存的过期时间之后,若存在这样的一种情况,就是处理业务逻辑的时间非常长,长到足以大于缓存的过期时间,这样,导致可能业务逻辑还没有处理完成,锁的缓存时间过期,锁被释放,其他线程拿到锁,导致数据不一致或释放其他线程锁的操作,因此,redisson出现了;
引入依赖:

	    <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.15.3</version>
        </dependency>

也可以用这个:这个必须在项目中配置RedissonClient类,如启动类中所写

		<dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.15.3</version>
        </dependency>

启动类

@SpringBootApplication
@MapperScan("com.ykq.distributedlock.dao")
public class DistributedLockApplication {

    public static void main(String[] args) {
        SpringApplication.run(DistributedLockApplication.class, args);
    }


    @Bean //Configuration
    public RedissonClient getRedisson(){
        Config config=new Config();
        config.useSingleServer().setAddress("redis://192.168.213.188:6379");
        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }
}

代码实现

@Service
public class TollServerWatchDog {
    @Resource
    private TollDao tollDao;

    @Resource
    private RedissonClient redissonClient;//解决了使用redis分布锁的过程中,产生的业务还没执行完,但是锁到期的问题

    public String selectById(Integer pid){
//        根据参数创建锁实例
        RLock lock = redissonClient.getLock("selectById::" + pid);
        try{
            lock.lock();
            Toll toll = tollDao.selectById(pid);
            if (toll.getCount()>0) {
                System.out.println("开始前"+toll.getCount());
               toll.setCount(toll.getCount()-1);
               tollDao.updateById(toll);
                System.out.println("剩余"+toll.getCount());
                return "库存减少成功";
            } else {
                return "库存不足";
            }
        }catch (Exception e){
            throw new RuntimeException(e.getMessage());
        }finally {
            if(lock.isLocked()){//判断锁是否处于锁定
                if(lock.isHeldByCurrentThread()){//判断是否时该进程自己的锁
                    lock.unlock();
                }
            }
        }
    }
}

补充说明:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里默认会获取看门狗机制的过期时间,为30s;
看门狗机制:就是redisson在调用.lock()时,可以设置过期时间,如果设置了时间,则按照设置的时间来过期,如果不设置,会开启看门狗机制,默认时间为30s,在过期时间*(1/3)时间里,看门狗会去判断逻辑是否执行完毕,若没有执行完,将过期时间再次设置为30s,一直到程序完成为止。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值