redisson分布式锁实现原理_redisson连接池

redisson分布式锁实现原理_redisson连接池redissonlock、tryLock分布式锁原理解析

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

近期在处理程序有两个不同来源入口的时候,因为容易产生并发情况,造成会有脏数据产生,在同事推荐下使用redisson的锁来解决并发问题。
先上使用的一定程度封装的工具类:

工具类

@Service
public class RedissonManager { 
   
    @Autowired
    private RedissonClient redissonClient;

    /** * 加锁 * * @param lockKey * @return */
    public RLock lock(String lockKey) { 
   
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
        return lock;
    }

    /** * 释放锁 * * @param lockKey */
    public void unlock(String lockKey) { 
   
        RLock lock = redissonClient.getLock(lockKey);
        lock.unlock();
    }

    /** * 释放锁 * * @param lock */
    public void unlock(RLock lock) { 
   
        lock.unlock();
    }

    /** * 带超时的锁 * * @param lockKey * @param timeout 超时时间 单位:秒 */
    public RLock lock(String lockKey, int timeout) { 
   
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout, TimeUnit.SECONDS);
        return lock;
    }

    /** * 带超时的锁 * * @param lockKey * @param unit 时间单位 * @param timeout 超时时间 */
    public RLock lock(String lockKey, TimeUnit unit, int timeout) { 
   
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout, unit);
        return lock;
    }

    /** * 尝试获取锁 * * @param lockKey * @param waitTime 最多等待时间 * @param leaseTime 上锁后自动释放锁时间 * @return */
    public boolean tryLock(String lockKey, int waitTime, int leaseTime) { 
   
        RLock lock = redissonClient.getLock(lockKey);
        try { 
   
            return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) { 
   
            return false;
        }
    }

    /** * 尝试获取锁 * * @param lockKey * @param unit 时间单位 * @param waitTime 最多等待时间 * @param leaseTime 上锁后自动释放锁时间 * @return */
    public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) { 
   
        RLock lock = redissonClient.getLock(lockKey);
        try { 
   
            return lock.tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) { 
   
            return false;
        }
    }

}

实际使用很简单,就是直接使用方法来锁住一个key,但是后续测试发现lock和tryLock是两种不同的情况。
lock是当获取锁失败时会阻塞当前进程,如果没有带参数设置过期时间则是30秒后自动解锁。
tryLock则是当获取锁失败时,当超过设置的等待时间时返回false

后面楼主出于好奇便看了一下redisson源码以及结合网上大神的见解,略为理解了一下,以此记录一下个人见解(不对请大家积极指出

lock

	private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException { 
   
	    // 此处为获取当前线程id
        long threadId = Thread.currentThread().getId();
        // 核心代码见下图后继续回来走逻辑
        Long ttl = tryAcquire(-1, leaseTime, unit, threadId);
        // 此处得到获取锁的结果,正常获取锁则ttl为null,竞争锁时返回锁的过期时间 
        if (ttl == null) { 
   
            return;
        }
		// 此处为订阅锁释放事件,
        // 如果当前线程通过 Redis 的 channel 订阅锁的释放事件获取得知已经被释放
        // 则会发消息通知待等待的线程进行竞争.
        RFuture<RedissonLockEntry> future = subscribe(threadId);
        if (interruptibly) { 
   
            commandExecutor.syncSubscriptionInterrupted(future);
        } else { 
   
            commandExecutor.syncSubscription(future);
        }

        try { 
   
            while (true) { 
   
            	// 此处循环重试获取锁,直至重新获取锁成功才跳出循环,
            	// 此种做法阻塞进程,一直处于等待锁手动释放或者超时才继续线程
                ttl = tryAcquire(-1, leaseTime, unit, threadId);
                // lock acquired
                if (ttl == null) { 
   
                    break;
                }

                // waiting for message
                if (ttl >= 0) { 
   
                    try { 
   
                        future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                    } catch (InterruptedException e) { 
   
                        if (interruptibly) { 
   
                            throw e;
                        }
                        future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                    }
                } else { 
   
                    if (interruptibly) { 
   
                        future.getNow().getLatch().acquire();
                    } else { 
   
                        future.getNow().getLatch().acquireUninterruptibly();
                    }
                }
            }
        } finally { 
   
        	// 最后释放订阅事件
            unsubscribe(future, threadId);
        }
// get(lockAsync(leaseTime, unit));
    }

tryLockInnerAsync

    <T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) { 
   
        return evalWriteAsync(getRawName(), LongCodec.INSTANCE, command,
                "if (redis.call('exists', KEYS[1]) == 0) then " +
                        "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                        "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                        "return nil; " +
                        "end; " +
                        "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
                        "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                        "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                        "return nil; " +
                        "end; " +
                        "return redis.call('pttl', KEYS[1]);",
                Collections.singletonList(getRawName()), unit.toMillis(leaseTime), getLockName(threadId));
    }

此段脚本为一段lua脚本:
结合个人理解其中的变量参数:
KEY[1]: 为你加锁的lock值
ARGV[2]: 为线程id
ARGV[1]: 为设置的过期时间

第一个if:
判断是否存在设置lock的key是否存在,不存在则利用redis的hash结构设置一个hash,值为1,并设置过期时间,后续返回锁。
第二个if:
判断是否存在设置lock的key是否存在,存在此线程的hash,则为这个锁的重入次数加1(将hash值+1),并重新设置过期时间,后续返回锁。
最后返回:
这个最后返回不是说最后结果返回,是代表以上两个if都没有进入,则代表处于竞争锁的情况,后续返回竞争锁的过期时间。

tryLock

trylock具有返回值,true或者false,表示是否成功获取锁。tryLock前期获取锁逻辑基本与lock一致,主要是后续获取锁失败的处理逻辑与lock不一致。

	 @Override
    public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException { 
   
        long time = unit.toMillis(waitTime);
        long current = System.currentTimeMillis();
        long threadId = Thread.currentThread().getId();
        Long ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
        // lock acquired
        if (ttl == null) { 
   
            return true;
        }
        // 以上与lock逻辑一致
		
		// 获取锁失败后,中途tryLock会一直判断中间操作耗时是否已经消耗锁的过期时间,如果消耗完则返回false
        time -= System.currentTimeMillis() - current;
        if (time <= 0) { 
   
            acquireFailed(waitTime, unit, threadId);
            return false;
        }
        
        current = System.currentTimeMillis();
        // 此处为订阅锁释放事件,
        // 如果当前线程通过 Redis 的 channel 订阅锁的释放事件获取得知已经被释放
        // 则会发消息通知待等待的线程进行竞争.
        RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);
        // 将订阅阻塞,阻塞时间设置为我们调用tryLock设置的最大等待时间,超过时间则返回false
        if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) { 
   
            if (!subscribeFuture.cancel(false)) { 
   
                subscribeFuture.onComplete((res, e) -> { 
   
                    if (e == null) { 
   
                        unsubscribe(subscribeFuture, threadId);
                    }
                });
            }
            acquireFailed(waitTime, unit, threadId);
            return false;
        }

        try { 
   
            time -= System.currentTimeMillis() - current;
            if (time <= 0) { 
   
                acquireFailed(waitTime, unit, threadId);
                return false;
            }
        
        	// 循环获取锁,但由于上面有最大等待时间限制,基本会在上面返回false
            while (true) { 
   
                long currentTime = System.currentTimeMillis();
                ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
                // lock acquired
                if (ttl == null) { 
   
                    return true;
                }

                time -= System.currentTimeMillis() - currentTime;
                if (time <= 0) { 
   
                    acquireFailed(waitTime, unit, threadId);
                    return false;
                }

                // waiting for message
                currentTime = System.currentTimeMillis();
                if (ttl >= 0 && ttl < time) { 
   
                    subscribeFuture.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                } else { 
   
                    subscribeFuture.getNow().getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);
                }

                time -= System.currentTimeMillis() - currentTime;
                if (time <= 0) { 
   
                    acquireFailed(waitTime, unit, threadId);
                    return false;
                }
            }
        } finally { 
   
            unsubscribe(subscribeFuture, threadId);
        }
// return get(tryLockAsync(waitTime, leaseTime, unit));
    }

结论

尽量在自己代码逻辑中添加解锁的逻辑,避免锁长时间存在浪费不必要的资源

综上所述,应尽量使用tryLock,且携带参数,因为可设置最大等待时间以及可及时获取加锁返回值,后续可做一些其他加锁失败的业务

以上是个人理解,如有不对,希望各位大神指出修改
在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/181595.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 滑动窗口求最大值 leetcode 59

    滑动窗口求最大值 leetcode 59滑动窗口最大值问题利用递减队列实现Dequeuedequeue=newLinkedList<>();递减队列方法说明peekFirst获取队头元素pollFirsr队头元素出队offerLast==add在队尾插入新元素publicint[]maxSlidingWindow(int[]nums,intk){if(nums.length==0){returnnewint[0];}

    2022年7月13日
    17
  • 浅谈Vue响应式原理

    浅谈Vue响应式原理一、Vue中的响应式Vue最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的JavaScript对象。而当你修改它们时,视图会进行更新。二、响应式的基本原理1.通过Object.definePropert来实现监听数据的改变和读取(属性中的getter和setter方法)实现数据劫持2.观察者模式(发布者-订阅者)观察者(订阅者)–Watcher:update():当事件发生时,具体要做的事情目标(发布者)–Dep:①subs数组:存储所有的观察者②

    2022年6月13日
    29
  • python偏函数理解_python中radians函数

    python偏函数理解_python中radians函数什么是偏函数partialpython中提供一种对于函数固定属性的函数偏函数的作用把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数偏函数的语法使用偏函数必须先导入from

    2022年7月31日
    6
  • 如何修改opkg 源

    如何修改opkg 源http://downloads.openwrt.org.cn/上面的链接是openwrt国内的源,但是只有适合以下几个系统的源Hacked/18-Jun-201413:58-OpenWrt-DreamBox/01-J

    2022年5月9日
    207
  • 2012年互联网创业者生存与发展报告

    2012年互联网创业者生存与发展报告创业者属性调查:部分90后接过80后创业接力棒80后仍是主力长江后浪推前浪,虽然当前80后仍是创业主力人群,但90后创业者已经开始显露头角。调查数据显示,2012年68%的创业者是80后,16%是90后,对比2011年调查数据(80后约为77%,90后创业者约为6%)发现,80后创业者数量下降10%,90后数量上升10%。创业动机调查:多数人对行业了解甚少仅凭兴趣创业

    2022年5月22日
    37
  • Vue.js学习的第一天

    Vue.js学习的第一天官网文档地址:传送门一、Vue介绍:1.Vue.js是什么?作者:2014年2月,尤雨溪曾就职于GoogleCreativeLabs和MeteorDevelopmentGroup。Vue(读音/vjuː/,类似于view)是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue被设计为可以自底向上逐层应用。Vue的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vu..

    2022年4月9日
    50

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号