redis各种分布式锁具体使用

注意点:锁的名称要起好(如加上当前涉及表记录id),锁的粒度,越细越快:具体缓存的是某个数据,11号商品

一、整合redisson作为分布式锁等功能框架

1.pom引入依赖(sgg159)

<!-- 以后使用redisson作为所有分布式锁,分布式对象等功能框架-->
<dependency>
     <groupId>org.redisson</groupId>
     <artifactId>redisson</artifactId>
     <version>3.11.1</version>
 </dependency>

2.配置类(sgg159)

可在配置文件中配置集群等,具体参考下面地址
https://github.com/redisson/redisson/wiki/2.-Configuration#25-replicated-mode
package com.product.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;

@Configuration
public class MyRedissonConfig {

    /**
     * 所有对 Redisson 的使用都是通过 RedissonClient
     *
     * @return
     * @throws IOException
     */
    @Bean(destroyMethod = "shutdown")//服务销毁会调用这个方法
    public RedissonClient redisson() throws IOException {
        // 1、创建配置
        Config config = new Config();
        // 注意redis的url格式  否则报错Redis url should start with redis:// or rediss://
        //redis使用单节点,可使用redis集群
        config.useSingleServer().setAddress("redis://192.168.163.131:6379");
        
        //使用redis集群
        //config.useClusterServers()
        //  .addNodeAddress("127,0.0.1:7004","127.0,.1:7001");
        
        // 2、根据 Config 创建出 RedissonClient 实例
        return Redisson.create(config);
    }
}

3.redis 分布式锁使用 只能同时一个在进行写操作(sgg160和sgg161)

@Controller
public class IndexController {

	@Autowired
    private RedissonClient redisson;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    @ResponseBody
    @GetMapping(value = "/hello")
    public String hello() {

        //1、获取一把锁,只要锁的名字一样,就是同一把锁
        RLock myLock = redisson.getLock("my-lock");

        //2、加锁
        //myLock.lock();      //阻塞式等待。默认加的锁都是30s
        //1)、锁的自动续期,如果业务超长,运行期间自动锁上新的30s。不用担心业务时间长,锁自动过期被删掉
        //2)、加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认会在30s内自动过期,不会产生死锁问题
        //3)、建议还是指定过期时间,理由是业务一般应该在30s之内执行完,同时可以省掉redis很多操作
        myLock.lock(30,TimeUnit.SECONDS);   //10秒钟自动解锁,自动解锁时间一定要大于业务执行时间
        //问题:在锁时间到了以后,不会自动续期
        //1、如果我们传递了锁的超时时间,就发送给redis执行脚本,进行占锁,默认超时就是 我们制定的时间
        //2、如果我们没有指定锁的超时时间,就使用 lockWatchdogTimeout = 30 * 1000 【看门狗默认时间】
        //   只要占锁成功,就会启动一个定时任务【重新给锁设置过期时间,新的过期时间就是看门狗的默认时间】,每隔10秒都会自动的再次续期,续成30秒
        // internalLockLeaseTime 【看门狗时间】 / 3, 10s
        try {
            System.out.println("加锁成功,执行业务..." + Thread.currentThread().getId());
            try { TimeUnit.SECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            //3、解锁  假设解锁代码没有运行,Redisson不会出现死锁问题
            System.out.println("释放锁..." + Thread.currentThread().getId());
            myLock.unlock();
        }
        return "hello";
    }
}

4.redis读写锁 (sgg162和sgg163)

@Controller
public class IndexController {

	@Autowired
    private RedissonClient redisson;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

 /**
     * 保证一定能读到最新数据,修改期间,写锁是一个排它锁(互斥锁、独享锁),读锁是一个共享锁
     * 写锁没释放读锁必须等待
     * 
     * 注意点:
     * (1)读 + 读 :相当于无锁,并发读,只会在Redis中记录好,所有当前的读锁。他们都会同时加锁成功
     * (2)写 + 读 :必须等待写锁释放
     * (3)写 + 写 :阻塞方式
     * (4)读 + 写 :有读锁。写也需要等待
     * 只要有读或者写的存都必须等待
     * @return
     */
    @GetMapping(value = "/write")
    @ResponseBody
    public String writeValue() {
        String s = "";
        //1.获取读写锁
        RReadWriteLock readWriteLock = redisson.getReadWriteLock("rw-lock");

        RLock rLock = readWriteLock.writeLock();
        try {
            //2、改数据加写锁,读数据加读锁
            rLock.lock();
            //3.业务处理
            s = UUID.randomUUID().toString();
            ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
            ops.set("writeValue",s);
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //4.释放锁
            rLock.unlock();
        }

        return s;
    }

    @GetMapping(value = "/read")
    @ResponseBody
    public String readValue() {
        String s = "";
        //1.获取读写锁
        RReadWriteLock readWriteLock = redisson.getReadWriteLock("rw-lock");

        RLock rLock = readWriteLock.readLock();
        try {
            //2.加读锁
            rLock.lock();
            //3.业务处理
            ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
            s = ops.get("writeValue");
            try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
             //4.释放锁
            rLock.unlock();
        }

        return s;
    }
}

5.redis闭锁(等其它几个结束再执行自己的) (sgg164)

@Controller
public class IndexController {

	@Autowired
    private RedissonClient redisson;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 放假、锁门
     * 1班没人了
     * 5个班,全部走完,我们才可以锁大门
     * 分布式闭锁
     */

    @GetMapping(value = "/lockDoor")
    @ResponseBody
    public String lockDoor() throws InterruptedException {

        RCountDownLatch door = redisson.getCountDownLatch("door");
        door.trySetCount(5);
        door.await();       //等待闭锁完成

        return "放假了...";
    }

    @GetMapping(value = "/gogogo/{id}")
    @ResponseBody
    public String gogogo(@PathVariable("id") Long id) {
        RCountDownLatch door = redisson.getCountDownLatch("door");
        door.countDown();       //计数-1

        return id + "班的人都走了...";
    }
}

6.redis信号量(等有位置才执行自己的) (sgg165)

@Controller
public class IndexController {

	@Autowired
    private RedissonClient redisson;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

  /**
     * 车库停车
     * 3车位
     * 信号量也可以做分布式限流
     */
    @GetMapping(value = "/park")
    @ResponseBody
    public String park() throws InterruptedException {

        RSemaphore park = redisson.getSemaphore("park");
        //park.acquire();阻塞,有位才会继续执行     //获取一个信号、获取一个值,占一个车位
        boolean flag = park.tryAcquire();//非阻塞

        if (flag) {
            //执行业务
        } else {
            return "error";
        }

        return "ok=>" + flag;
    }

    @GetMapping(value = "/go")
    @ResponseBody
    public String go() {
        RSemaphore park = redisson.getSemaphore("park");
        park.release();     //释放一个车位
        return "ok";
    }
}

7.注意点:锁的粒度(sgg166)

 /**
     * 缓存里的数据如何和数据库的数据保持一致??
     * 缓存数据一致性
     * 1)、双写模式
     * 2)、失效模式
     *
     * @return
     */
    public Map<String, List<Catalogs2Vo>> getCatalogJsonFromDbWithRedissonLock() {

        //1、占分布式锁。去redis占坑
        //(锁的粒度,越细越快:具体缓存的是某个数据,11号商品) product-11-lock
        //RLock catalogJsonLock = redissonClient.getLock("catalogJson-lock");
        //创建读锁
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("catalogJson-lock");
        RLock rLock = readWriteLock.readLock();

        Map<String, List<Catalogs2Vo>> dataFromDb = null;
        try {
            //2.加锁成功
            rLock.lock();
            //3.执行业务
            dataFromDb = getCatalogJsonFromDB();
        } finally {
            //4.释放锁
            rLock.unlock();
        }
        return dataFromDb;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值