精华内容
下载资源
问答
  • redisson分布式锁

    2020-10-28 17:39:24
    redisson分布式锁 参考链接 <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.11.5</version> </dependency> 1:...

    redisson分布式锁

    参考链接

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

    1: 生成redissonClient对象给spring容器

    import org.redisson.Redisson;
    import org.redisson.api.RedissonClient;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /** redisson 配置
     */
    @Configuration
    public class RedissonConfig {
        @Value("${spring.redis.host}")
        private String host;
    
        @Bean
        public RedissonClient redissonClient() {
            org.redisson.config.Config config = new org.redisson.config.Config();
            config.useSingleServer().setAddress("redis://"+host+":6379").setPassword("123456");
            //添加主从配置
    //        config.useMasterSlaveServers().setMasterAddress("").setPassword("").addSlaveAddress(new String[]{"",""});
            RedissonClient redissonClient = Redisson.create(config);
            return redissonClient;
        }
    

    基本用法:

    public String get(@RequestBody UseCodeeCmd cmd) {
            //redisson锁 
            RLock lock = redissonClient.getLock("lockKey");//获取锁对象
            
            //尝试加锁(默认30秒过期,如果业务方法没执行完释放锁,开启(子线程)看门狗自动延长过期时间)
            boolean b1 = lock.tryLock();
            //没获取到锁直接返回
            if(!b1){return new JsonResult<>(401,"系统忙,请稍后再试",null);}
            try {
                JsonResult result = 业务方法
                return result;
            }finally {
                lock.unlock();//释放锁,不是本线程加的释放不了
            }
        }
    
    展开全文
  • redisson 分布式锁

    万次阅读 2018-05-04 18:43:29
    需求背景: 在分布式多机部署的情况下,我们要求某一个方法,只能同一个时间只能被一台机器的一个线程执行。...这时,就需要借助介质redisson,基于redis的分布式锁。。 前提不多说了,先安装好redis,使用...

    需求背景:

    在分布式多机部署的情况下,我们要求某一个方法,只能同一个时间只能被一台机器的一个线程执行。

    在单机中,有synchronized,读写锁等方式可以解决同步问题。 但是,这些只能作用在同一个机器上,只能保证某一个机器中的方法,不会同时执行。多台机器还是可以同时执行。

    这时,就需要借助介质redisson,基于redis的分布式锁。。

    原理分析

    redisson是一个在redis基础上实现的java驻内存网格(in-memory data grid ),提供了分布式和可扩展性的java数据结构

    特点

    基于netty实现,采用非阻塞IO。
    支持连接池、pipeline 、lua scripting 、redis sentinel、redis cluster
    不支持事务,官方建议使用Lua脚本的原子操作代替
    主从、哨兵、集群都支持。

    实现

    • 加锁
    public static void main(String[] args) throws InterruptedException {
            RLock rLock=redissonClient.getLock("updateAccount");
    
            // 最多等待100秒、上锁10s以后解锁
            if(rLock.tryLock(100,10, TimeUnit.SECONDS)){
                System.out.println("获取锁成功");
            }
            Thread.sleep(20000);
            rLock.unlock();
    
            redissonClient.shutdown();
        }
    

    tryLock

    tryLock里面有三个参数

        boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException;
    
    1. watiTime: 等待锁的最大时间,超过时间不再尝试获取所。
    2. leaseTime:如果没有手动调用unlock,超过时间自动释放
    3. TimeUnit :释放时间单位。

    lock

    如果直接使用Lock(),不带参数,如果不手动unlock,线程就会一直持有锁,redis设计为了防止宕机,不能释放锁的情况,就在这个方法里面设置了一个默认的过期时间,30秒。
    但是巧妙的地方在于这里,如果此时线程已经获取锁,但是应用没有执行完,redis会在30秒后自动释放锁吗? 答案是不会的,因为在lock方法里面,当线程获取锁成功后,就会有一个定时任务(默认10秒/次),将过期时间刷新,继续等待30秒。直到锁被手动unlock,或者kill 线程了,就会不再刷新时间了。

    具体分析可以参考这个连接参考连接

        @Override
        public void lock() {
            try {
                lockInterruptibly();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    
    @Override
        public void lockInterruptibly() throws InterruptedException {
            lockInterruptibly(-1, null);
        }
    
        @Override
        public void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException {
            long threadId = Thread.currentThread().getId();
            //尝试获取锁
            Long ttl = tryAcquire(leaseTime, unit, threadId);
            // lock acquired
            if (ttl == null) { // 获取锁成功直接返回
                return;
            }
    
            RFuture<RedissonLockEntry> future = subscribe(threadId);
            commandExecutor.syncSubscription(future);
    
            try {
                while (true) {
                    ttl = tryAcquire(leaseTime, unit, threadId);
                    // lock acquired
                    if (ttl == null) {
                        break;
                    }
    
                    // waiting for message
                    if (ttl >= 0) {
                        getEntry(threadId).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                    } else {
                        getEntry(threadId).getLatch().acquire();
                    }
                }
            } finally {
                unsubscribe(future, threadId);
            }
    //        get(lockAsync(leaseTime, unit));
        }
    

    实战

    前提不多说了,先安装好redis,使用的Redis主从+哨兵模式。这里多台机器安装的redis哨兵,也可以单机多端口安装

    1.1首先创建RedissonManager 获取redisson

    package com.test.redisson.manager;
    import org.redisson.Redisson;
    import org.redisson.api.RedissonClient;
    import org.redisson.config.Config;
    /**
     * @version 1.0.0
     * @description
     * @date 2018/05/04 16:54
     **/
    public class RedissonManager {
    
        private static RedissonClient redissonClient;
        private static Config config = new Config();
        /**
         * 初始化Redisson,使用哨兵模式
         */
        public static void init(){
            try {
                config.useSentinelServers()
                        .setMasterName("cache")
                        .addSentinelAddress("10.0.0.1:26379","10.0.0.2:26379", "10.0.0.3:26379")
                        //同任何节点建立连接时的等待超时。时间单位是毫秒。默认:10000
                        .setConnectTimeout(30000)
                        //当与某个节点的连接断开时,等待与其重新建立连接的时间间隔。时间单位是毫秒。默认:3000
                        .setReconnectionTimeout(10000)
                        //等待节点回复命令的时间。该时间从命令发送成功时开始计时。默认:3000
                        .setTimeout(10000)
                        //如果尝试达到 retryAttempts(命令失败重试次数) 仍然不能将命令发送至某个指定的节点时,将抛出错误。如果尝试在此限制之内发送成功,则开始启用 timeout(命令等待超时) 计时。默认值:3
                        .setRetryAttempts(5)
                        //在一条命令发送失败以后,等待重试发送的时间间隔。时间单位是毫秒。     默认值:1500
                        .setRetryInterval(3000)
                ;
                redissonClient = Redisson.create(config);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        /**
         * 获取Redisson的实例对象
         * @return
         */
        public static Redisson getRedisson(){
            init();
            return (Redisson) redissonClient;
        }
        /**
         * 测试Redisson是否正常
         */
        public static void main(String[] args) {
            Redisson redisson = RedissonManager.getRedisson();
            System.out.println("redisson = " + redisson);
        }
    }
    

    1.2 锁操作LockUtil

    package com.test.redisson.lockutil;
    import com.test.redisson.manager.RedissonManager;
    import org.redisson.Redisson;
    import org.redisson.api.RLock;
    import java.util.concurrent.TimeUnit;
    /**
     * Redisson分布式锁 工具类
     */
    public class LockUtil {
        private static Redisson redisson = RedissonManager.getRedisson();
        /**
         * 根据name对进行上锁操作,redisson Lock 一直等待获取锁
         * @param lockname
         */
        public static void lock(String lockname) throws InterruptedException {
            String key = lockname;
            RLock lock = redisson.getLock(key);
            //lock提供带timeout参数,timeout结束强制解锁,防止死锁 
            lock.lock(60L, TimeUnit.SECONDS);
        }
    
        /**
         * 根据name对进行上锁操作,redisson tryLock  根据第一个参数,一定时间内为获取到锁,则不再等待直接返回boolean。交给上层处理
         * @param lockname
         */
        public static boolean tryLock(String lockname) throws InterruptedException {
            String key = lockname;
            RLock lock = redisson.getLock(key);
            //tryLock,第一个参数是等待时间,5秒内获取不到锁,则直接返回。 第二个参数 60是60秒后强制释放
            return lock.tryLock(5L,60L, TimeUnit.SECONDS);
        }
        /**
         * 根据name对进行解锁操作
         * @param lockname
         */
        public static void unlock(String lockname){
            String key = lockname;
            RLock lock = redisson.getLock(key);
            lock.unlock();
        }
    }
    
    

    1.3 RedissonTestLock

    package com.test.redisson.test;
    import com.test.redisson.lockutil.LockUtil;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    /**
     * @version 1.0.0
     * @description
     * @date 2018/05/04 17:02
     **/
    public class RedissonTestLock {
        public static void main(String[] args) {
            //模拟2个线程
            for (int i = 1; i <= 2; i++) {
                //可以开2个IDE,分别测试以下三个方法
                //打开2个IDE同时执行时,这里可以分别取不同名,区分
                new Thread("IDE-ONE-"+i) {
                    @Override
                    public void run() {
                        /**
                         * 测试testLock结果,每个IDE中线程,依次排队等待获取锁。然后执行任务
                         */
                        testLock("redissonlocktest_testkey");
    
                        /**
                         * 测试testTryLock结果,每个IDE中线程,在TryLock的等待时间范围内,若获取到锁,返回true,则执行任务;若获取不到,则返回false,直接返回return;
                         */
    //                    testTryLock("redissonlocktest_testkey");
    
                        /**
                         * 测试testSyncro结果,IDE之间的线程互不影响,同一个IDE中的线程排队值执行,不同IDE之间的互补影响,可同时执行
                         */
    //                    testSyncro("redissonlocktest_testkey");
                    }
                }.start();
            }
        }
    
        //测试lock,拿不到lock就不罢休,不然线程就一直block。
        public static void testLock(String preKey) {
            try {
                System.out.println(getDate()+Thread.currentThread().getName() + "准备开始任务。。。。");
                LockUtil.lock(preKey);
                System.out.println(getDate()+Thread.currentThread().getName() + "模拟正在执行任务。。。。");
                Thread.sleep(5000);//等待5秒,后面的所有线程都“依次”等待5秒,等待获取锁,执行任务
    
            } catch (Exception e) {
                System.out.println(getDate()+"线程锁 :" + Thread.currentThread().getId() + " exception :" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                e.printStackTrace();
            } finally {
                LockUtil.unlock(preKey);
                System.out.println(getDate()+Thread.currentThread().getName() + "释放。。。。");
            }
        }
    
        //带时间限制的tryLock(),拿不到lock,就等一段时间,超时返回false。
        public static void testTryLock(String preKey) {
    
            try {
                System.out.println(getDate()+Thread.currentThread().getName() + "准备开始任务。。。。");
                boolean falg = LockUtil.tryLock(preKey);
                //这里若获取不到锁,就直接返回了
                if(!falg){
                    System.out.println(getDate()+Thread.currentThread().getName() + "--没有获取到锁直接返回--" + falg);
                    return;
                }
                System.out.println(getDate()+Thread.currentThread().getName() + "--获取锁--" + falg);
                System.out.println(getDate()+Thread.currentThread().getName() + "模拟正在执行任务。。。。");
    
               //由于在LockUtil.tryLock设置的等待时间是5s,所以这里如果休眠的小于5秒,这第二个线程能获取到锁,
                // 如果设置的大于5秒,则剩下的线程都不能获取锁。可以分别试试2s,和8s的情况
                Thread.sleep(8000);//等待6秒
    
            } catch (Exception e) {
                System.out.println(getDate()+"线程锁 :" + Thread.currentThread().getId() + " exception :" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                e.printStackTrace();
            } finally {
                try {
                    LockUtil.unlock(preKey);
                    System.out.println(getDate()+Thread.currentThread().getName() + "释放。。。。");
                }catch (Exception e){
                    e.printStackTrace();
                }
    
            }
        }
    
        //synchronized 这种锁只能锁住同一台机器的线程,若部署多台机器,则不能锁住
        public static void testSyncro(String preKey) {
            synchronized (preKey.intern()){//为什么要intern前篇文章有解释
                try {
                    System.out.println(getDate()+Thread.currentThread().getName() + "准备开始任务。。。。");
                    System.out.println(getDate()+Thread.currentThread().getName() + "--获取锁--" );
                    System.out.println(getDate()+Thread.currentThread().getName() + "模拟正在执行任务。。。。");
                    Thread.sleep(6000);//执行2秒
                } catch (Exception e) {
                    System.out.println(getDate()+"线程锁 :" + Thread.currentThread().getId() + " exception :" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                    e.printStackTrace();
                } finally {
                    try {
                        System.out.println(getDate()+Thread.currentThread().getName() + "释放。。。。");
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        }
        public static String getDate(){
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"   ";
        }
    }
    
    

    **2.1 当两个IDE同时执行testLock **
    IDE-ONE
    这里写图片描述
    IDE-TWO
    这里写图片描述

    根据上面时间,可以看到上面2个IDE中的4个线程,都是依次等候执行

    2.2 当两个IDE同时执行testTryLock
    IDE-ONE
    这里写图片描述

    IDE-TWO
    这里写图片描述

    可以看到,在IDE-ONE中,先获取到锁后,休眠了8秒,后面线程,在锁的等待时间5秒内(时间在LockUtil.trylock有设置),无法获取到锁。

    2.3 当两个IDE同时测试testSyncro
    IDE-ONE

    这里写图片描述

    IDE-TWO
    这里写图片描述

    可以看到,在同一个IDE中线程,是排队执行。在不同IDE中,是可以同时执行的。这时就体现了上面分布式锁的作用了

    展开全文
  • 给大家分享了关于Redisson分布式锁源码相关的知识点内容,有兴趣的朋友们可以参考学习下。
  • Redisson分布式锁 之前的基于注解的锁有一种锁是基本redis的分布式锁,这篇文章主要介绍了Java使用Redisson分布式锁实现原理,非常具有实用价值,需要的朋友可以参考下
  • Redisson 分布式锁

    2020-12-04 01:15:22
    使用 Redisson 作为所有分布式锁、分布式对象等功能的框架 Redisson实现了JUC锁的接口 pom <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> ...


    前期配置


    使用 Redisson 作为所有分布式锁、分布式对象等功能的框架

    Redisson实现了JUC锁的接口

    • pom
    <dependency>
    	<groupId>org.redisson</groupId>
     	<artifactId>redisson</artifactId>
      	<version>3.12.5</version>
    </dependency>
    
    • Config 程序化配置 Redisson
    /***
     *  程序化配置 Redisson
     */
    @Configuration
    public class MyRedissonConfig {
    
        //所有对Redisson的使用都是通过RedissonClient对象
        @Bean(destroyMethod = "shutdown")
        public RedissonClient redisson(){
            //1、创建配置
            Config config = new Config();
                //集群模式配置
    //        config.useClusterServers().addNodeAddress("127.0.0.1:7004","127.0.0.1:7001";
                //单节点模式配置
            // 注意:Redis url should start with redis:// or rediss:// (for SSL connection)
            config.useSingleServer().setAddress("redis://192.168.56.10:6379");
    
            //2、根据Config创建出RedissonClient实例
            RedissonClient redissonClient = Redisson.create(config);
    
            return redissonClient;
        }
    }
    
    • 测试类 导入Autowired
    @Autowired
    RedissonClient redisson;
    
    @Autowired
    StringRedisTemplate redisTemplate;
    

    1、lock锁 + 看门狗机制


    • 测试
        //测试简单服务 用于调优
        //redisson 测试锁机制
        @ResponseBody
        @GetMapping("/hello")
        public String hello(){
            //1、获取一把锁,只要锁名一样,就是同一把锁
            RLock lock = redisson.getLock("my-lock");
          
            //2、加锁
            lock.lock(); //阻塞式等待 默认的加锁时长是30s 锁自动过期被删掉
    
            /* 注意:这样写不会自动续期   【自动续期时间一定要大于业务时间】 */
    //        lock.lock(10, TimeUnit.SECONDS); //设置10s自动解锁 推荐
          
            try {
    
                System.out.println("加锁成功,执行业务... " + Thread.currentThread().getId());
                Thread.sleep(30000);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                //3、解锁
                lock.unlock();
                System.out.println("释放锁--- " + Thread.currentThread().getId());
            }
            return "hello";
        }
    }
    
    • 输出
    加锁成功,执行业务... 94
    释放锁--- 94
    加锁成功,执行业务... 94
    释放锁--- 94
    加锁成功,执行业务... 96
    释放锁--- 96
    

    lock.lock(); 阻塞式等待 默认的加锁时长是30s 会执行看门狗

    1. redisson的自动续期,如果业务超长,运行期间自动续上30s,不用担心业务时间长,锁自动过期被删掉
    2. 加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认在30s后自动删除
    3. 如果我们未指定锁的超时时间,底层就使用lockWatchdogTimeout = 30 * 1000;【看门狗默认时间】只要站锁成功,就会启动定时任务(重新给锁设置过期时间)新的时间就是看门狗的默认时间,每10秒,都会自动续期续成满时间

    lock.lock(10, TimeUnit.SECONDS); 设置10s自动解锁 不会执行看门狗

    1. 注意:这样写不会自动续期自动续期时间一定要大于业务时间
    2. 如果我们指定锁的超时时间,就发送给redis执行脚本,进行占锁,默认时间就是我我们传递的时间
    3. 不过还是推荐指定过期时间。省掉了整个续期操作。我们可以将解锁时间和业务时间保持一致:例如此处案例,业务时间为30s,我们可以这样写:lock.lock(30, TimeUnit.SECONDS); 省掉续期操作,手动解锁

    Redisson1


    看门狗机制

    • lock.lock();

    Redisson2


    2、读锁 + 写锁


    保证一定能读到最新的数据,修改期间,写锁是一个排他锁(互斥锁),读锁是一个共享锁写锁没释放读就必须等待

    • 读 + 读:相当于无锁,并发读,只会在redis中记录好,所有当前的读锁,他们都会同时加锁成功
    • 写 + 读:读锁需要等待写锁释放
    • 写 + 写:阻塞方式
    • 读 + 写:写锁需要等待读锁释放

    写锁:

        @ResponseBody
        @GetMapping("/write")
        public String writeValue() {
            //1、获取一把锁,只要锁名一样,就是同一把锁
            RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
    
            String s = "";
            //1、改数据加写锁,读数据加读锁
            RLock wLock = lock.writeLock(); //拿到写锁
            try {
                wLock.lock(); //加锁
                System.out.println("写锁加锁成功......" + Thread.currentThread().getId());
                s = UUID.randomUUID().toString();
                Thread.sleep(30000); //睡眠30s
                redisTemplate.opsForValue().set("writeValue", s);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                wLock.unlock(); //解锁
                System.out.println("写锁解锁..." + Thread.currentThread().getId());
    
            }
            return s;
        } 
    

    读锁:

    	@ResponseBody
        @GetMapping("/read")
        public String readValue() {
            //1、获取一把锁,只要锁名一样,就是同一把锁
            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(30 000); //睡眠30s
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                rLock.unlock(); //解锁
                System.out.println("读锁解锁---" + Thread.currentThread().getId());
    
            }
            return s;
        }
    

    • JUC的写锁:new ReentrantReadWriteLock().writeLock()

    • JUC的读锁:new ReentrantReadWriteLock().readLock()


    3、信号量


    tryAcquire() : 当没有空闲时,直接返回false,不会等待
    acquire() : 当没有空闲时,会等待信号量的释放


        //信号量测试
        /***
         *  车库停车 --> 拿到车位
         *  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();
            return "ok"; //释放一个车位
        }
    
    

    Redisson3


    tryAcquire() --> 信号量也可以用作分布式限流

    boolean b = park.tryAcquire();
    if (b){
    	//执行业务 
    }else {
    	return "error";
    }
    return "ok => " + b ;
    

    Redisson4

    JUC的信号量:

    • 车位数量:Semaphore semaphore = new Semaphore(5);
    • 释放车位:semaphore.release();
    • 占用车位:semaphore.acquire();

    4、闭锁


    //闭锁测试
        /***
         * 模拟场景:5个班级的人都走完了才可以锁大门
         */
        @GetMapping("/lockDoor")
        @ResponseBody
        public String lockDoor() throws InterruptedException {
    
            RCountDownLatch door = redisson.getCountDownLatch("door"); //拿到闭锁
    
            door.trySetCount(5); //等待的次数
            door.await(); //等待闭锁都完成
    
            return "放假了......";
        }
    
        @GetMapping("/gogogo/{id}")
        @ResponseBody
        public String gogogo(@PathVariable("id") Long id){
            RCountDownLatch door = redisson.getCountDownLatch("door"); //拿到闭锁
            door.countDown(); //计数-1
            return id + "班的人走了......";
        }
    

    在这里插入图片描述

    • JUC的闭锁:CountDownLatch()

    5、缓存一致性问题


    双写模式

    在这里插入图片描述

    失效模式

    Redisson7


    解决方案

    无论是双写模式还是失效模式,都会导致缓存的不一致问题。即多个实例同时更新会出事。怎么办?

    • 1、如果是用户纬度数据(订单数据、用户数据),这种并发几率非常小,不用考虑这个问题,缓存数据加上过期时间,每隔一段时间触发读的主动更新即可
    • 2、如果是菜单,商品介绍等基础数据,也可以去使用canal订阅binlog的方式。
    • 3、缓存数据+过期时间也足够解决大部分业务对于缓存的要求。
    • 4、通过加锁保证并发读写,写写的时候按顺序排好队。读读无所谓。所以适合使用读写锁。(业务不关心脏数据,允许临时脏数据可忽略);

    总结:

    • 我们能放入缓存的数据本就不应该是实时性、一致性要求超高的。所以缓存数据的时候加上过期时间,保证每天拿到当前最新数据即可。
    • 我们不应该过度设计,增加系统的复杂性
    • 遇到实时性、一致性要求高的数据,就应该查数据库,即使慢点。

    使用cache解决缓存一致性问题

    在这里插入图片描述


    案例: 使用Redisson的锁机制优化三级分类数据的查询

    一定要注意锁的名字 --> 只要lock的名字一样,锁就一样

    • 锁的粒度,越细越快
    • 锁的粒度:具体缓存的是某个数据 实例:具体缓存的是11号商品 那么锁名应为 product-11-lock
        /***
         *  缓存里面的数据如何和数据库保持一致 --> 缓存数据一致性问题
         *  1)、双写模式
         *  2)、失效模式
         */
        public Map<String, List<Catelog2Vo>> getCatalogJsonDBWithRedissonLock() {
    
            //1、占分布式锁,去Redis占坑
            /* 一定要注意锁的名字 --> 只要lock的名字一样,锁就一样
             */
            RLock lock = redisson.getLock("catalogJson-lock");
            lock.lock(); //加锁
            Map<String, List<Catelog2Vo>> dataFromDb;
            try {
                dataFromDb = getDataFromDb();
            } finally {
                // 判断是否还在锁定状态
    		    if (lock.isLocked()){
    		        // 锁是否被当前线程持有
    		        if (lock.isHeldByCurrentThread()){
    		            // 解锁
    		            lock.unlock();
    		        }
    		    }
            }
            return dataFromDb;
        }
    

    注意 【可能会出现此异常 Error

    IllegalMonitorstateException:attemot to unlock lock.not locked by current thread by node id:oda685f-81a…

    • 出现这个错误的原因:是在并发多的时候就可能会遇到这种错误,可能会被重新抢占
    • 不见得当前这个锁的状态还是在锁定,并且是本线程持有
    • 解决如下:
    } finally {
        // 判断是否还在锁定状态
        if (lock.isLocked()){
            // 锁是否被当前线程持有
            if (lock.isHeldByCurrentThread()){
                // 解锁
                lock.unlock();
            }
        }
    }
    
    展开全文
  • Redisson分布式锁

    2021-03-25 09:38:33
    这里写自定义目录标题什么是RedissonJedis与Redisson常用功能分布式锁限流器话题(订阅分发)事务队列分布式服务布隆过滤器配置方式单Redis节点模式spring boot配置案例讲解帮助文档 什么是Redisson Redisson是一个...

    什么是Redisson

    • Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。
    • 它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。包括Queue、Lock、AtomicLong、CountDownLatch、Publish / Subscribe、Executor service等,提供了使用Redis的最简单和最便捷的方法。
    • Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。
    • Redisson底层采用的是Netty框架,支持Redis 2.8以上版本,支持Java1.6+以上版本。

    Jedis与Redisson

    • Jedis:Jedis 只是简单的封装了 Redis 的API库,可以看作是Redis客户端,它的方法和Redis 的命令很类似,相比于Redisson 更原生一些,更灵活。
    • Redisson:Redisson 不仅封装了 redis ,还封装了对更多数据结构的支持,以及锁等功能,相比于Jedis 更加大。

    常用功能

    分布式锁

    1、可重入锁:以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的。
    2、公平锁:每个线程抢占锁的顺序为先后调用lock方法的顺序依次获取锁,类似于排队吃饭。
    3、闭锁:基于Redisson的Redisson分布式闭锁(CountDownLatch)Java对象RCountDownLatch采用了与java.util.concurrent.CountDownLatch相似的接口和用法。

    限流器

    基于Redis的分布式限流器(RateLimiter)可以用来在分布式环境下现在请求方的调用频率。既适用于不同Redisson实例下的多线程限流,也适用于相同Redisson实例下的多线程限流。该算法不保证公平性。除了同步接口外,还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。

    话题(订阅分发)

    订阅话题后当接收到消息进行消费

    事务

    为RMap、RMapCache、RLocalCachedMap、RSet、RSetCache和RBucket这样的对象提供了具有ACID属性的事务功能Redisson事务通过分布式锁保证了连续写入的原子性,同时在内部通过操作指令队列实现了Redis原本没有的提交与滚回功能当提交与滚回遇到问题的时候,将通过org.redisson.transaction.TransactionException告知用户

    队列

    有界无界、阻塞非阻塞队列

    分布式服务

    • 分布式远程服务:可以用来通过共享接口执行存在于另一个Redisson实例里的对象方法。换句话说就是通过Redis实现了Java的远程过程调用(RPC)。分布式远程服务基于可以用POJO对象,方法的参数和返回类不受限制,可以是任何类型。
    • 执行任务服务:Redisson的分布式执行服务实现了java.util.concurrent.ExecutorService接口,支持在不同的独立节点里执行基于java.util.concurrent.Callable接口 或java.lang.Runnable接口或Lambda的任务。这样的任务也可以通过使用Redisson实例,实现对储存在Redis里的数据进行操作。Redisson分布式执行服务是最快速和有效执行分布式运算的方法。
    • 调度任务服务:Redisson的分布式调度任务服务实现了java.util.concurrent.ScheduledExecutorService接口,支持在不同的独立节点 里执行基于java.util.concurrent.Callable接口或java.lang.Runnable接口的任务。Redisson独立节点按顺序 运行Redis列队里的任务。调度任务是一种需要在未来某个指定时间运行一次或多次的特殊任务。

    布隆过滤器

    本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你“某样东西一定不存在或者可能存在”。相比于传统的 List、Set、Map 等数据结构,它更高效、占用空间更少,但是缺点是其返回的结果是概率性的,而不是确切的。

    配置方式

    单Redis节点模式

    //默认连接地址 127.0.0.1:6379
    RedissonClient redisson = Redisson.create();
    Config config = new Config();
    config.useSingleServer().setAddress("myredisserver:6379");
    RedissonClient redisson = Redisson.create(config);
    

    spring boot配置

    spring:
      redis:
        host:127.0.0.1
        password:123456
        database:8
    
    <dependency>
      <groupld>org.springframework.boot</groupld>
      <artifactld>spring-boot-starter-data-redis</artifactld>
    </dependency>
    <dependency>
      <groupld>org.redisson</groupld>
      <artifactld>redisson-spring-boot-starter</artifactld>
      <version>3.12.5</version>
    </dependency>
    

    案例讲解

    • 立刻返回获取锁的状态
    • 等待返回获取锁的状态
    • 限流器

    帮助文档

    Redisson wiki:
    https://github.com/redisson/redisson/wiki/目录

    展开全文
  • Redisson分布式锁的简单使用

    万次阅读 热门讨论 2017-04-12 10:06:48
    redis分布式锁学习和集成框架Redisson分布式锁的一些简单使用
  • Redisson 概述 Redisson 分布式锁快速入门
  • Redisson分布式锁看门狗机制 源码分析 Redisson分布式锁看门狗机制     我们在上篇文章中分析了Redisson加锁和解锁的流程,总体看逻辑还算清晰,主要是使用异步执行lua脚本进行加锁,但是其中有些细节,...
  • 具有很好的参考价值,下面跟着小编一起来看下吧Redisson分布式锁之前的基于注解的锁有一种锁是基本redis的分布式锁,锁的实现我是基于redisson组件提供的RLock,这篇来看看redisson是如何实现锁的。不同版本实现锁的...
  • 集成redisson分布式锁

    2019-05-05 14:49:55
    目录一、redisson是什么二、为什么要用redisson分布式锁(一)为什么用锁(二)为什么用分布式锁(三)为什么用redisson分布式锁三、redisson分布式锁怎么用(一)引入相关jar包(二)编写配置文件1、一般spring项目...
  • Redisson分布式锁的原理:有关Redisson作为实现分布式锁,总的分3大模块来讲。1、Redisson实现分布式锁原理2、Redisson实现分布式锁的源码解析3、Redisson实现分布式锁的项目代码(可以用于实际项目中)本文只介绍...
  • 文章目录Redisson 分布式锁实现分析分布式锁常见问题互斥死锁性能重入RedissonLock 源码解析分布式锁使用*getLock()**lock()**tryAcquire()**unlock()*注意参考 分布式锁常见问题 互斥 分布式系统中运行着多个节点,...
  • Redisson分布式锁学习

    2021-03-29 22:49:39
    1、缓存-分布式锁-Redisson-lock看门狗原理-redisson如何解决死锁 https://www.pianshen.com/article/82441844942/ 2、Redisson 分布式锁实战与 watch dog 机制解读 https://blog.51cto.com/15018721/2647285 ...
  • Redisson分布式锁的源码分析

    千次阅读 2020-04-02 14:41:45
    Redisson分布式锁的源码分析 Redisson 分布式锁实现思路 锁标识:Hash 数据结构,key 为锁的名字,filed 当前竞争锁成功线程的唯一标识,value 重入次数 队列:所有竞争锁失败的线程,会订阅当前锁的解锁事件,利用 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,723
精华内容 1,089
关键字:

redisson分布式锁

redis 订阅