java——redis随笔——实战——优惠券秒杀——分布式锁——基于redis
注意:synchronized用户单机(jvm)上面的锁,对于分布式应用则无能为力。所以对于分布式系统,则需要分布式锁。
-
分布式锁:满足分布式系统或集群模式下多线程课件并且可以互斥的锁
-
分布式锁的核心思想就是让大家共用同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路
Redis:Redis作为分布式锁是非常常见的一种使用方式,现在企业级开发中基本都是用Redis或者Zookeeper作为分布式锁,利用SETNX
这个方法,
如果插入Key成功,则表示获得到了锁,如果有人插入成功,那么其他人就回插入失败,无法获取到锁,利用这套逻辑完成互斥
,从而实现分布式锁;
public class SimpleRedisLock implements ILock { //锁的前缀 private static final String KEY_PREFIX = "lock:"; //具体业务名称,将前缀和业务名拼接之后当做Key private String name; //这里不是@Autowired注入,采用的是构造器注入,在创建SimpleRedisLock时,将RedisTemplate作为参数传入 private StringRedisTemplate stringRedisTemplate; public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) { this.name = name; this.stringRedisTemplate = stringRedisTemplate; } @Override public boolean tryLock(long timeoutSec) { //获取线程标识 long threadId = Thread.currentThread().getId(); //获取锁,使用SETNX方法进行加锁,同时设置过期时间,防止死锁 Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId + "", timeoutSec, TimeUnit.SECONDS); //自动拆箱可能会出现null,这样写更稳妥 return Boolean.TRUE.equals(success); } @Override public void unlock() { //通过DEL来删除锁 stringRedisTemplate.delete(KEY_PREFIX + name); } }
@Override public Result seckillVoucher(Long voucherId) { LambdaQueryWrapper<SeckillVoucher> queryWrapper = new LambdaQueryWrapper<>(); //1. 查询优惠券 queryWrapper.eq(SeckillVoucher::getVoucherId, voucherId); SeckillVoucher seckillVoucher = seckillVoucherService.getOne(queryWrapper); //2. 判断秒杀时间是否开始 if (LocalDateTime.now().isBefore(seckillVoucher.getBeginTime())) { return Result.fail("秒杀还未开始,请耐心等待"); } //3. 判断秒杀时间是否结束 if (LocalDateTime.now().isAfter(seckillVoucher.getEndTime())) { return Result.fail("秒杀已经结束!"); } //4. 判断库存是否充足 if (seckillVoucher.getStock() < 1) { return Result.fail("优惠券已被抢光了哦,下次记得手速快点"); } Long userId = UserHolder.getUser().getId(); // 创建锁对象 SimpleRedisLock redisLock = new SimpleRedisLock("order:" + userId, stringRedisTemplate); // 获取锁对象 boolean isLock = redisLock.tryLock(120); // 加锁失败,说明当前用户开了多个线程抢优惠券,但是由于key是SETNX的,所以不能创建key,得等key的TTL到期或释放锁(删除key) if (!isLock) { return Result.fail("不允许抢多张优惠券"); } try { // 获取代理对象 IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy(); return proxy.createVoucherOrder(voucherId); } finally { // 释放锁 redisLock.unlock(); } }
private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-"; @Override public boolean tryLock(long timeoutSec) { // 获取线程标识 String threadId = ID_PREFIX + Thread.currentThread().getId(); // 获取锁 Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS); return Boolean.TRUE.equals(success); } @Override public void unlock() { // 获取当前线程的标识 String threadId = ID_PREFIX + Thread.currentThread().getId(); // 获取锁中的标识 String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name); // 判断标识是否一致 if (threadId.equals(id)) { // 释放锁 stringRedisTemplate.delete(KEY_PREFIX + name); } }
分类:
复习——随笔
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)