谷粒商城项目总结--分布式锁Redisson

本文总结了谷粒商城项目中使用Redisson实现分布式锁的经验,包括可重入锁、公平锁、读写锁、闭锁和信号量的详细使用方法,通过这些工具有效解决了并发控制问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、自己模拟一个分布式锁

   /**
     * @Description 自己模拟一个分布式锁
     * 加锁使用setIfAbsent setnx(setIfPresent setex)
     * 值不要写固定字符串 使用大字符串如 UUID
     * 解锁使用lua脚本
     */
    public Map<String, List<Category2Vo>> getCategoryJsonFromDbWithRedisLock() {
        String uuid = UUID.randomUUID().toString();
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid, 300, TimeUnit.SECONDS);
        if (lock) {
            //原子加锁 原子解锁
            System.out.println("获取分布式锁成功");
            Map<String, List<Category2Vo>> dataFromDb;
            try {
                //加锁成功 执行业务
                dataFromDb = getDataFromDb();
            } finally {
                String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                        "then\n" +
                        "    return redis.call(\"del\",KEYS[1])\n" +
                        "else\n" +
                        "    return 0\n" +
                        "end";
                stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), uuid);
            }
            //获取值对比 对比成功删除 必须是一个原子操作    lua脚本解锁
//            String lockValue = stringRedisTemplate.opsForValue().get("lock");
//            if (uuid.equals(lockValue)){
//                //如果是自己的锁 删除
//                stringRedisTemplate.delete("lock");
//            }
            return getDataFromDb();
        } else {
            System.out.println("获取分布式锁失败 等待重试");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return getCategoryJsonFromDbWithRedisLock();
        }
    }

二、Redisson 完成分布式锁

官方文档

1.导入依赖

 <!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.14.1</version>
        </dependency>

2.配置

@Configuration
public class MyRedissonConfig {
    /**
     * config.useSingleServer() 单节点模式
     * @Description 所有对Redisson操作都是通过RedissonClient对象
     * @Param destroyMethod = "shutdown" 销毁方法 服务停止销毁
     * @return org.redisson.api.RedissonClient
     */
    @Bean(destroyMethod = "shutdown")
    RedissonClient redisson() throws IOException {
        //创建配置
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.0.105:6379");
        //创建实例
        return Redisson.create(config);
    }
}

3.使用

3.1 可重入锁

redisson默认就是可重入锁

 @Autowired
    RedissonClient redisson;
 @ResponseBody
    @GetMapping("/hello")
    public String hello() {
    
    //获取一把锁 只要名字一样就是一把锁
        RLock lock = redisson.getLock("MY-LOCK");
     //lock.lock();
    //加锁 阻塞式等待 默认过期时间30s
    //锁自动续期 如果业务超长 运行期间自动续上新的30s 不用担心业务时间长锁自动过期被删除
    //加锁的业务只要运行完成就不会给当前锁续期 即使不手动解锁 默认30s删除
    
    lock.lock(10, TimeUnit.SECONDS);
         //10s自动解锁 自动解锁时间一定要大于业务执行时间
        //问题 lock.lock(10, TimeUnit.SECONDS); 锁时间到了以后不会自动续期
        //1.如果传了锁的超时时间 就发给reids执行脚本 进行占锁 默认超时时间就是我们指定的时间
        //2.如果没传锁的超时时间 就使用30*1000    LockWatchdogTimeout看门狗的默认时间
             //只要占锁成功 就会启动一个定时任务 (重新给锁设定过期时间 新的过期时间就是看门狗的默认时间)
         //定时任务时间 = internalLockLeaseTime(看门狗时间 )/ 3 10s
        //最佳实战  lock.lock(30, TimeUnit.SECONDS); 省掉了整个续期操作 自动解锁给长一点 手动解锁
        try {
            System.out.println("加锁成功 执行业务" + Thread.currentThread().getId());
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//解锁
            System.out.println("释放锁" + Thread.currentThread().getId());
        }
        return "hello";
    }
3.2 公平锁

redisson.getFairLock()

3.3 读写锁
	/**
     * 保证一定能读的最新数据 
     * 修改期间 写锁是一个排他锁(互斥锁) 读锁是一个共享锁
     * 写锁没释放 读锁就必须等待
     * 读 + 读 相当于无锁 并发读只会在reids中记录好当前的读锁 都会同时加锁成功
     * 写 + 读 等待写锁释放
     * 写 + 写 阻塞方式
     * 读 + 写 有读锁 写也需要等待
     */
     
    @ResponseBody
    @GetMapping("/read")
    public String read() {

        RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
        String s = "";
        RLock rLock = lock.readLock();
        rLock.lock();
        try {
            System.out.println("读锁加锁成功 执行业务" + Thread.currentThread().getId());
            s = redisTemplate.opsForValue().get("writeValue");
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();//解锁
            System.out.println("读锁释放锁" + Thread.currentThread().getId());
        }
        return s;
    }

    @ResponseBody
    @GetMapping("/write")
    public String write() {
        RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
        String s = "";
        RLock rLock = lock.writeLock();
        rLock.lock();
        try {
            System.out.println("写锁加锁成功 执行业务" + Thread.currentThread().getId());
            s = UUID.randomUUID().toString();
            Thread.sleep(30000);
            redisTemplate.opsForValue().set("writeValue", s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();//解锁
            System.out.println("写锁释放锁" + Thread.currentThread().getId());
        }
        return s;
    }
3.4 闭锁
  /**
     * @Description 放假锁门 闭锁
     */
    @ResponseBody
    @GetMapping("/lockDoor")
    public String lockDoor() throws InterruptedException {
        RCountDownLatch countDownLatch = redisson.getCountDownLatch("door");
        countDownLatch.trySetCount(5);
        countDownLatch.await();//等待闭锁都完成
        return "放假了";
    }

    @ResponseBody
    @GetMapping("/gogogo/{id}")
    public String gogogo(@PathVariable("id") Long id){
        RCountDownLatch countDownLatch = redisson.getCountDownLatch("door");
        countDownLatch.countDown();//计数减一
        return "ok"+id;
    }
3.5 信号量

可以用来限流 比如当前服务只能承受1w的并发请求
设置1w个信号量
让所有服务先获取信号量 能获取到证明有空闲线程来处理 否则等待

/**
     * @Description 模拟停车 限流 信号量
     */
    @ResponseBody
    @GetMapping("/park")
    public String park() throws InterruptedException {
        RSemaphore park = redisson.getSemaphore("park");
        park.acquire();//获取一个信号量 获取一个值 阻塞式获取 一定要获取 
        park.tryAcquire();//有就获取 没有就算了
        return "ok";
    }

    @ResponseBody
    @GetMapping("/go")
    public String go(){
        RSemaphore park = redisson.getSemaphore("park");
        park.release();//释放一个信号量 释放一个值
        return "ok";
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值