sprintboot redisson

1 、简介 Redisson 是架设在 Redis 基础上 的一个 Java 驻内存数据网格( In-Memory Data Grid )。充分 的利用了 Redis 键值数据库提供的一系列优势, 基于 Java 实用工具包中常用接口 ,为使用者 提供了一系列具有分布式特性的常用工具类。 使得原本作为协调单机多线程并发程序的工 具包获得了协调分布式多机多线程并发系统的能力 ,大大降低了设计和研发大规模分布式 系统的难度。同时结合各富特色的分布式服务,更进一步简化了分布式环境中程序相互之间 的协作。 官方文档: https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95    

第一步:

 <!--引入redis-->

<dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.12.0</version>
</dependency>

 

第二步:config下面创建MyRedissonConfig

package com.atguigu.gulimall.product.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;

@Configuration
public class MyRedissonConfig {
    @Bean(destroyMethod="shutdown")
    public RedissonClient redisson() throws IOException {
        //1、创建配置
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.56.10:6379");
        //2、根据Config创建出RedissonClient示例
        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }
}

 


第三步:测试

@Autowired
StringRedisTemplate stringRedisTemplate;
@Test
public void teststringRedisTemplate(){
    /**
     *
     */
    //hello   world
    ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
    //保存
    ops.set("hello","world_"+ UUID.randomUUID().toString());

    //查询
    String hello = ops.get("hello");
    System.out.println("之前保存的数据是:"+hello);

}
 

sprintboot redisson

 

 

进一步分析redisson锁的机制:

引入:

@Autowired
RedissonClient redisson;

@Autowired
StringRedisTemplate redisTemplate;

案例一:

    @ResponseBody
    @GetMapping("/hello")
    public String hello(){
        //1、获取一把锁,只要锁的名字一样,就是同一把锁
        RLock lock = redisson.getLock("my-lock");

        //2、加锁
        lock.lock(); //阻塞式等待。默认加的锁都是30s时间。
        //1)、锁的自动续期,如果业务超长,运行期间自动给锁续上新的30s。不用担心业务时间长,锁自动过期被删掉
        //2)、加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认在30s以后自动删除。

//        lock.lock(10,TimeUnit.SECONDS); //10秒自动解锁,自动解锁时间一定要大于业务的执行时间。
        //问题:lock.lock(10,TimeUnit.SECONDS); 在锁时间到了以后,不会自动续期。
        //1、如果我们传递了锁的超时时间,就发送给redis执行脚本,进行占锁,默认超时就是我们指定的时间
        //2、如果我们未指定锁的超时时间,就使用30 * 1000【LockWatchdogTimeout看门狗的默认时间】;
        //    只要占锁成功,就会启动一个定时任务【重新给锁设置过期时间,新的过期时间就是看门狗的默认时间】,每隔10s都会自动再次续期,续成30s
        //    internalLockLeaseTime【看门狗时间】 / 3,10s

        //最佳实战
        //1)、lock.lock(30,TimeUnit.SECONDS);省掉了整个续期操作。手动解锁
        try{
            //Thread.currentThread()是Thread的一个静态方法,用来获取当前线程对象的一个引用。
            System.out.println("加锁成功,执行业务..."+Thread.currentThread().getId());
            Thread.sleep(30000);
        }catch (Exception e){

        }finally {
            //3、解锁  将设解锁代码没有运行,redisson会不会出现死锁
            System.out.println("释放锁..."+Thread.currentThread().getId());//
            lock.unlock();
        }

        return "hello";
    }

案例二:

    //保证一定能读到最新数据,修改期间,写锁是一个排他锁(互斥锁、独享锁)。读锁是一个共享锁
    //写锁没释放读就必须等待
    // 读 + 读: 相当于无锁,并发读,只会在redis中记录好,所有当前的读锁。他们都会同时加锁成功
    // 写 + 读: 等待写锁释放
    // 写 + 写: 阻塞方式
    // 读 + 写: 有读锁。写也需要等待。
    // 只要有写的存在,都必须等待
    @GetMapping("/write")
    @ResponseBody
    public String writeValue(){
        RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
        String s = "";
        RLock rLock = lock.writeLock();
        try {
            //1、改数据加写锁,读数据加读锁
            rLock.lock();
            System.out.println("写锁加锁成功..."+Thread.currentThread().getId());
            s = UUID.randomUUID().toString();
            Thread.sleep(30000);
            redisTemplate.opsForValue().set("writeValue",s);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
            System.out.println("写锁释放"+Thread.currentThread().getId());
        }

        return  s;
    }

    @GetMapping("/read")
    @ResponseBody
    public String readValue(){
        RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
//        ReentrantReadWriteLock writeLock = new ReentrantReadWriteLock();
        String s = "";
        //加读锁
        RLock rLock = lock.readLock();
        rLock.lock();
        try {
            System.out.println("读锁加锁成功"+Thread.currentThread().getId());
            s = redisTemplate.opsForValue().get("writeValue");
            Thread.sleep(30000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
            System.out.println("读锁释放"+Thread.currentThread().getId());
        }

        return  s;
    }

案例三:

   /**
     * 车库停车,
     * 3车位
     * 信号量也可以用作分布式限流;
     */
    @GetMapping("/park")
    @ResponseBody
    public String park() throws InterruptedException {
        RSemaphore park = redisson.getSemaphore("park");
//        park.acquire();//获取一个信号,获取一个值,占一个车位
        boolean b = park.tryAcquire();
        if(b){
            //执行业务
        }else {
            return "error";
        }

        return "ok=>"+b;
    }

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

        //
//        Semaphore semaphore = new Semaphore(5);
//        semaphore.release();
//
//        semaphore.acquire();

        return "ok";
    }

案例四:

    /**
     * 放假,锁门
     * 1班没人了,2
     * 5个班全部走完,我们可以锁大门
     */
    @GetMapping("/lockDoor")
    @ResponseBody
    public String lockDoor() throws InterruptedException {
        RCountDownLatch door = redisson.getCountDownLatch("door");
        door.trySetCount(5);
        door.await(); //等待闭锁都完成

        return "放假了...";
    }

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

//        CountDownLatch

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

 

 

上一篇:SprintBoot简单入门


下一篇:结合第一篇文章“FeignException“自定义处理遇到的坑。