Java基于Redis实现分布式锁(原子性操作、续命)——90%以上都搞错了

在使用分布式锁之前,要先思考一个问题,我们为什么要使用分布式锁?

这是因为,在分布式的部署环境下,原来的这个synchronized 只能在当前的JVM中加锁,不能跨JVM实现加锁,所以这种情况下我们就急需要使用分布式的锁来完成锁的功能。

分布式锁有很多种实现方式,基于zookeeper、基于数据库排他锁、基于缓存redis/memcache...

我们使用基于缓存的redis实现分布式锁。

public class TrainTickService {

    private Logger logger= LoggerFactory.getLogger(TrainTickService.class);

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    //这个是锁对象
    @Autowired
    private DistributeRedisLock distributeRedisLock;
    /**
     * 这个方法就是专门用来减售火车票的这样一个系统
     * 秒杀抢火车票
     *
     * 30秒没有运行完 怎么办?
     */
    public String produceStock(){
        String lock="lock";
        String value=UUID.randomUUID().toString();
        MyThread myThread=null;
        //设置个字符串
        try {
            // setnx命令
            //Boolean tag = stringRedisTemplate.opsForValue().setIfAbsent(lock, "");
            //给这个key设置过期时间
            //执行下面这一句话的时候突然死了?  是不是又出现死锁了
            //stringRedisTemplate.expire(lock,30,TimeUnit.SECONDS);
            //这个命令的底层实际上 也运行的是 咋们的命令  在底层他是怎样来实现原子性的呢?
            // 充分的利用了 redis和lua脚本  在C的层面上来实现原子性的a
            //setnx  只有这个可以不存在的时候 这个才会被删除
            Boolean tag=stringRedisTemplate.opsForValue().setIfAbsent(lock,value,30,TimeUnit.SECONDS);
            if (!tag) {
                //递归调用(让来的人中就要抢我们数据库中拥有的这个值)
                produceStock();
                return "目前排队人数过多...请稍后重试";
            }
            //超过了15
            //开一个守护线程
            myThread = new MyThread(lock);
            myThread.setDaemon(true);
            myThread.start();

            // 每隔设置时间的3分支1就进行线程的续命

            //一会初始值的时候我将火车票  放到redis中去
            //减去库存
            //去Redis中将库存数据给取出来
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("traintickes"));
            //首先要判断下 这个库存是否>0
            if (stock > 0) {  //说明可以减去库存
                int rStock = stock - 1;
                //下一步:将真实的库存放到咋们的Redis中去
                stringRedisTemplate.opsForValue().set("traintickes", String.valueOf(rStock));
                logger.info("扣减库存成功....剩余库存:" + rStock);

            } else {   //说明不能扣减库存
                logger.info("库存扣减失败、库存是负数、不足...");
            }  //已经用了15秒钟

        }finally {
            if(value.equals(stringRedisTemplate.opsForValue().get(lock))){
                //在这里要中断这个线程
                myThread.stop();
                stringRedisTemplate.delete(lock);
                logger.info("释放锁成功....");
            }
        }
        return "抢票成功....";

    }

    /**
     * 使用后台线程进行续命
     * 守护线程
     *    在主线程下 如果有一个守护线程  这个守护线程的生命周期 跟主线程是同生死的
     */
    class MyThread extends Thread{
        String lock;
        public MyThread(String lock){
            this.lock=lock;
        }
        @Override
        public void run() {
            while (true){
                //干这个事情、
                try{
                    Thread.sleep(10000);
                }catch (Exception err){
                }
                //假设程序还活着  那么说明需要续命
                logger.info("续费中....");
                stringRedisTemplate.expire(lock,30,TimeUnit.SECONDS);
            }
        }
    }


    /**
     * 使用redisson来完成
     * @return
     */
    public String produceStockRedisson(){
        String lock="lock";
        try {
            boolean lock1 = distributeRedisLock.lock(lock);
            if(true==lock1){//说明加锁成功
                int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("traintickes"));
                //首先要判断下 这个库存是否>0
                if (stock > 0) {  //说明可以减去库存
                    int rStock = stock - 1;
                    //下一步:将真实的库存放到咋们的Redis中去
                    stringRedisTemplate.opsForValue().set("traintickes", String.valueOf(rStock));
                    logger.info("扣减库存成功....剩余库存:" + rStock);

                } else {   //说明不能扣减库存
                    logger.info("库存扣减失败、库存是负数、不足...");
                }  //已经用了15秒钟
            }else{
                return "当前的排队人数过多...";
            }
        }finally {
            distributeRedisLock.unlock(lock);
        }
        return "抢票成功....";

    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

遥夜人间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值