分布式锁解决集群下一人一单超卖问题

由于本项目是专门学习Redis的,所以这里会使用Redis的setnx指令实现分布式锁解决超卖问题

创建分布式锁

java 复制代码
public class SimpleRedisLock implements ILock {

    private String name;
    private StringRedisTemplate stringRedisTemplate;
    private static final String KEY_PREFIX = "lock:";

    public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {
        this.name = name;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean tryLock(long timeoutSec) {
        String key = KEY_PREFIX + name;
        String value = Thread.currentThread().getId() + "";
        Boolean res = stringRedisTemplate.opsForValue()
                .setIfAbsent(key, value, timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(res);
    }

    @Override
    public void unlock() {
        String key = KEY_PREFIX + name;
        stringRedisTemplate.delete(key);
    }
}

使用分布式锁 :改造前面VoucherOrderServiceImpl中的代码,将之前使用sychronized锁的地方,改成自己实现的分布式锁:

java 复制代码
/**
     * 抢购秒杀券
     */
    @Override
    public Result seckillVoucher(Long voucherId) {
        // 1、查询秒杀券
        SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
        // 2、判断秒杀券是否合法
        if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {
            // 秒杀券的开始时间在当前时间之后
            return Result.fail("秒杀尚未开始");
        }
        if (voucher.getEndTime().isBefore(LocalDateTime.now())) {
            // 秒杀券的结束时间在当前时间之前
            return Result.fail("秒杀已结束");
        }
        // 3、判断库存是否充足
        if (voucher.getStock() < 1) {
            return Result.fail("秒杀券已抢空");
        }
        Long userId = UserHolder.getUser().getId();
        // 去字符串常量池找字符串对象,使得加锁同一个对象
        // 先获取锁,再开启事务,事务结束后,才会释放锁
        String key = "order:" + userId;
        // 锁定范围是用户ID
        SimpleRedisLock lock = new SimpleRedisLock(key, stringRedisTemplate);
        boolean isLock = lock.tryLock(1200);
        if(!isLock){
            // 获取锁失败,返回错误或重试,但此时是同一个用户并发多个请求,应该返回错误
            return Result.fail("不允许重复下单");
        }
        // 获取锁成功
        try{
            // spring的事务是基于代理对象的,这里直接调用相当于this.xxx,并非代理对象,因此事务不会生效,所以要拿到代理对象
            IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
            return proxy.createVoucherOrder(voucherId);
        }finally {
            lock.unlock();
        }
    }

重启程序,在postman中使用同一个用户的token发送两次请求,可以发现只有有一个用户获取锁成功。

相关推荐
weixin_4365250719 分钟前
Gitee - IDEA 主支 master 和分支 dev 的使用
java·ide·intellij-idea
Terio_my1 小时前
RabbitMQ 安装与配置
分布式·rabbitmq
sheji34161 小时前
【开题答辩全过程】以 YF精品视频动漫平台为例,包含答辩的问题和答案
java·eclipse
小蕾Java1 小时前
Java 开发工具,最新2025 IDEA 使用
java·ide·intellij-idea
是席木木啊1 小时前
Idea升级到2024版本:“marketplace plugins are not loaded”解决方案
java·ide·intellij-idea
胚芽鞘6811 小时前
博客标题:解密 IntelliJ IDEA 调试:当你的 List 不仅仅是 List
java·ide·intellij-idea
꒰ঌ 安卓开发໒꒱1 小时前
Java面试-并发面试(一)
java·jvm·面试
HezhezhiyuLe1 小时前
IDEA中Add New Configuration找不到Tomcat
java·ide·intellij-idea
悦悦子a啊1 小时前
[Java]PTA: jmu-Java-02基本语法-08-ArrayList入门
java·开发语言·算法
235161 小时前
【MySQL】数据库事务深度解析:从四大特性到隔离级别的实现逻辑
java·数据库·后端·mysql·java-ee