国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

微服務(wù)系列文章之 Redisson實現(xiàn)分布式鎖(2)

這篇具有很好參考價值的文章主要介紹了微服務(wù)系列文章之 Redisson實現(xiàn)分布式鎖(2)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

一、RLock接口

1、概念
public interface RLock extends Lock, RExpirable, RLockAsync

很明顯RLock是繼承Lock鎖,所以他有Lock鎖的所有特性,比如lock、unlock、trylock等特性,同時它還有很多新特性:強制鎖釋放,帶有效期的鎖,。

2、RLock鎖API

這里針對上面做個整理,這里列舉幾個常用的接口說明

public interface RLock {
    //----------------------Lock接口方法-----------------------

    /**
     * 加鎖 鎖的有效期默認30秒
     */
    void lock();
    /**
     * tryLock()方法是有返回值的,它表示用來嘗試獲取鎖,如果獲取成功,則返回true,如果獲取失?。存i已被其他線程獲?。?,則返回false .
     */
    boolean tryLock();
    /**
     * tryLock(long time, TimeUnit unit)方法和tryLock()方法是類似的,只不過區(qū)別在于這個方法在拿不到鎖時會等待一定的時間,
     * 在時間期限之內(nèi)如果還拿不到鎖,就返回false。如果如果一開始拿到鎖或者在等待期間內(nèi)拿到了鎖,則返回true。
     *
     * @param time 等待時間
     * @param unit 時間單位 小時、分、秒、毫秒等
     */
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    /**
     * 解鎖
     */
    void unlock();
    /**
     * 中斷鎖 表示該鎖可以被中斷 假如A和B同時調(diào)這個方法,A獲取鎖,B為獲取鎖,那么B線程可以通過
     * Thread.currentThread().interrupt(); 方法真正中斷該線程
     */
    void lockInterruptibly();

    //----------------------RLock接口方法-----------------------
    /**
     * 加鎖 上面是默認30秒這里可以手動設(shè)置鎖的有效時間
     *
     * @param leaseTime 鎖有效時間
     * @param unit      時間單位 小時、分、秒、毫秒等
     */
    void lock(long leaseTime, TimeUnit unit);
    /**
     * 這里比上面多一個參數(shù),多添加一個鎖的有效時間
     *
     * @param waitTime  等待時間
     * @param leaseTime 鎖有效時間
     * @param unit      時間單位 小時、分、秒、毫秒等
     */
    boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException;
    /**
     * 檢驗該鎖是否被線程使用,如果被使用返回True
     */
    boolean isLocked();
    /**
     * 檢查當前線程是否獲得此鎖(這個和上面的區(qū)別就是該方法可以判斷是否當前線程獲得此鎖,而不是此鎖是否被線程占有)
     * 這個比上面那個實用
     */
    boolean isHeldByCurrentThread();
    /**
     * 中斷鎖 和上面中斷鎖差不多,只是這里如果獲得鎖成功,添加鎖的有效時間
     * @param leaseTime  鎖有效時間
     * @param unit       時間單位 小時、分、秒、毫秒等
     */
    void lockInterruptibly(long leaseTime, TimeUnit unit);  
}

RLock相關(guān)接口,主要是新添加了?leaseTime?屬性字段,主要是用來設(shè)置鎖的過期時間,避免死鎖。

二、RedissonLock實現(xiàn)類

public class RedissonLock extends RedissonExpirable implements RLock

RedissonLock實現(xiàn)了RLock接口,所以實現(xiàn)了接口的具體方法。這里我列舉幾個方法說明下

1、void lock()方法
 @Override
    public void lock() {
        try {
            lockInterruptibly();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

發(fā)現(xiàn)lock鎖里面進去其實用的是lockInterruptibly(中斷鎖,表示可以被中斷),而且捕獲異常后用 Thread.currentThread().interrupt()來真正中斷當前線程,其實它們是搭配一起使用的。

具體有關(guān)lockInterruptibly()方法講解推薦一個博客。博客:Lock的lockInterruptibly()

接下來執(zhí)行流程,這里理下關(guān)鍵幾步

/**
     * 1、帶上默認值調(diào)另一個中斷鎖方法
     */
    @Override
    public void lockInterruptibly() throws InterruptedException {
        lockInterruptibly(-1, null);
    }
    /**
     * 2、另一個中斷鎖的方法
     */
    void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException 
    /**
     * 3、這里已經(jīng)設(shè)置了鎖的有效時間默認為30秒  (commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout()=30)
     */
    RFuture<Long> ttlRemainingFuture = tryLockInnerAsync(commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
    /**
     * 4、最后通過lua腳本訪問Redis,保證操作的原子性
     */
    <T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
        internalLockLeaseTime = unit.toMillis(leaseTime);

        return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
                "if (redis.call('exists', KEYS[1]) == 0) then " +
                        "redis.call('hset', 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.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
    }

那么void lock(long leaseTime, TimeUnit unit)方法其實和上面很相似了,就是從上面第二步開始的。

2、tryLock(long waitTime, long leaseTime, TimeUnit unit)

接口的參數(shù)和含義上面已經(jīng)說過了,現(xiàn)在我們開看下源碼,這里只顯示一些重要邏輯。

 @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(leaseTime, unit, threadId);
        //1、 獲取鎖同時獲取成功的情況下,和lock(...)方法是一樣的 直接返回True,獲取鎖False再往下走
        if (ttl == null) {
            return true;
        }
        //2、如果超過了嘗試獲取鎖的等待時間,當然返回false 了。
        time -= System.currentTimeMillis() - current;
        if (time <= 0) {
            acquireFailed(threadId);
            return false;
        }

        // 3、訂閱監(jiān)聽redis消息,并且創(chuàng)建RedissonLockEntry,其中RedissonLockEntry中比較關(guān)鍵的是一個 Semaphore屬性對象,用來控制本地的鎖請求的信號量同步,返回的是netty框架的Future實現(xiàn)。
        final RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);
        //  阻塞等待subscribe的future的結(jié)果對象,如果subscribe方法調(diào)用超過了time,說明已經(jīng)超過了客戶端設(shè)置的最大wait time,則直接返回false,取消訂閱,不再繼續(xù)申請鎖了。
        //  只有await返回true,才進入循環(huán)嘗試獲取鎖
        if (!await(subscribeFuture, time, TimeUnit.MILLISECONDS)) {
            if (!subscribeFuture.cancel(false)) {
                subscribeFuture.addListener(new FutureListener<RedissonLockEntry>() {
                    @Override
                    public void operationComplete(Future<RedissonLockEntry> future) throws Exception {
                        if (subscribeFuture.isSuccess()) {
                            unsubscribe(subscribeFuture, threadId);
                        }
                    }
                });
            }
            acquireFailed(threadId);
            return false;
        }

       //4、如果沒有超過嘗試獲取鎖的等待時間,那么通過While一直獲取鎖。最終只會有兩種結(jié)果
        //1)、在等待時間內(nèi)獲取鎖成功 返回true。2)等待時間結(jié)束了還沒有獲取到鎖那么返回false。
        while (true) {
            long currentTime = System.currentTimeMillis();
            ttl = tryAcquire(leaseTime, unit, threadId);
            // 獲取鎖成功
            if (ttl == null) {
                return true;
            }
           //   獲取鎖失敗
            time -= System.currentTimeMillis() - currentTime;
            if (time <= 0) {
                acquireFailed(threadId);
                return false;
            }
        }
    }

重點?tryLock一般用于特定滿足需求的場合,但不建議作為一般需求的分布式鎖,一般分布式鎖建議用void lock(long leaseTime, TimeUnit unit)。因為從性能上考慮,在高并發(fā)情況下后者效率是前者的好幾倍

3、unlock()

解鎖的邏輯很簡單。

@Override
    public void unlock() {
        // 1.通過 Lua 腳本執(zhí)行 Redis 命令釋放鎖
        Boolean opStatus = commandExecutor.evalWrite(getName(), LongCodec.INSTANCE,
                RedisCommands.EVAL_BOOLEAN,
                "if (redis.call('exists', KEYS[1]) == 0) then " +
                        "redis.call('publish', KEYS[2], ARGV[1]); " +
                        "return 1; " +
                        "end;" +
                        "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
                        "return nil;" +
                        "end; " +
                        "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +
                        "if (counter > 0) then " +
                        "redis.call('pexpire', KEYS[1], ARGV[2]); " +
                        "return 0; " +
                        "else " +
                        "redis.call('del', KEYS[1]); " +
                        "redis.call('publish', KEYS[2], ARGV[1]); " +
                        "return 1; "+
                        "end; " +
                        "return nil;",
                Arrays.<Object>asList(getName(), getChannelName()),
                LockPubSub.unlockMessage, internalLockLeaseTime,
                getLockName(Thread.currentThread().getId()));
        // 2.非鎖的持有者釋放鎖時拋出異常
        if (opStatus == null) {
            throw new IllegalMonitorStateException(
                    "attempt to unlock lock, not locked by current thread by node id: "
                            + id + " thread-id: " + Thread.currentThread().getId());
        }
        // 3.釋放鎖后取消刷新鎖失效時間的調(diào)度任務(wù)
        if (opStatus) {
            cancelExpirationRenewal();
        }
    }

使用 EVAL 命令執(zhí)行 Lua 腳本來釋放鎖:

  1. key 不存在,說明鎖已釋放,直接執(zhí)行?publish?命令發(fā)布釋放鎖消息并返回?1。
  2. key 存在,但是 field 在 Hash 中不存在,說明自己不是鎖持有者,無權(quán)釋放鎖,返回?nil
  3. 因為鎖可重入,所以釋放鎖時不能把所有已獲取的鎖全都釋放掉,一次只能釋放一把鎖,因此執(zhí)行?hincrby?對鎖的值減一
  4. 釋放一把鎖后,如果還有剩余的鎖,則刷新鎖的失效時間并返回?0;如果剛才釋放的已經(jīng)是最后一把鎖,則執(zhí)行?del?命令刪除鎖的 key,并發(fā)布鎖釋放消息,返回?1

注意這里有個實際開發(fā)過程中,容易出現(xiàn)很容易出現(xiàn)上面第二步異常,非鎖的持有者釋放鎖時拋出異常。比如下面這種情況文章來源地址http://www.zghlxwxcb.cn/news/detail-560066.html

   //設(shè)置鎖1秒過去
        redissonLock.lock("redisson", 1);
        /**
         * 業(yè)務(wù)邏輯需要咨詢2秒
         */
        redissonLock.release("redisson");
      /**
       * 線程1 進來獲得鎖后,線程一切正常并沒有宕機,但它的業(yè)務(wù)邏輯需要執(zhí)行2秒,這就會有個問題,在 線程1 執(zhí)行1秒后,這個鎖就自動過期了,
       * 那么這個時候 線程2 進來了。在線程1去解鎖就會拋上面這個異常(因為解鎖和當前鎖已經(jīng)不是同一線程了)
       */

到了這里,關(guān)于微服務(wù)系列文章之 Redisson實現(xiàn)分布式鎖(2)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • Redisson實現(xiàn)分布式鎖示例

    Redisson實現(xiàn)分布式鎖示例

    可以下載redis desktop manager軟件來查看redis里面存放的東西 紅色框內(nèi)的TTL值就是過期時間,默認-1,表示永不過期,指定過期時間后就變成你指定的值了。 上面的方法,我們讓線程睡眠60S,代表我們的業(yè)務(wù)執(zhí)行時間,在調(diào)用這個方法時,我們可以在 redis desktop manager軟件上實時查

    2024年02月12日
    瀏覽(22)
  • redisson+aop實現(xiàn)分布式鎖

    基于注解實現(xiàn),一個注解搞定緩存 Aop:面向切面編程,在不改變核心代碼的基礎(chǔ)上實現(xiàn)擴展,有以下應(yīng)用場景 ①事務(wù) ②日志 ③controlleradvice+expetcationhandle實現(xiàn)全局異常 ④redissson+aop實現(xiàn)分布式鎖 ⑤認證授權(quán) Aop的實現(xiàn)存在與bean的后置處理器beanpostprocessAfterinitlazing 注解的定義仿照

    2024年01月19日
    瀏覽(26)
  • SpringBoot結(jié)合Redisson實現(xiàn)分布式鎖

    SpringBoot結(jié)合Redisson實現(xiàn)分布式鎖

    ?????作者名稱:DaenCode ??作者簡介:啥技術(shù)都喜歡搗鼓搗鼓,喜歡分享技術(shù)、經(jīng)驗、生活。 ??人生感悟:嘗盡人生百味,方知世間冷暖。 ??所屬專欄:SpringBoot實戰(zhàn) 以下是專欄部分內(nèi)容,更多內(nèi)容請前往專欄查看! 標題 一文帶你學(xué)會使用SpringBoot+Avue實現(xiàn)短信通知功能

    2024年02月08日
    瀏覽(25)
  • 圖解Redisson如何實現(xiàn)分布式鎖、鎖續(xù)約?

    圖解Redisson如何實現(xiàn)分布式鎖、鎖續(xù)約?

    使用當前(2022年12月初)最新的版本:3.18.1; 案例 案例采用redis-cluster集群的方式; redission支持4種連接redis方式,分別為單機、主從、Sentinel、Cluster 集群;在分布式鎖的實現(xiàn)上區(qū)別在于hash槽的獲取方式。 具體配置方式見Redisson的GitHub(https://github.com/redisson/redisson/wiki/2.-%E9

    2023年04月16日
    瀏覽(31)
  • Spring Boot 集成 Redisson 實現(xiàn)分布式鎖

    Spring Boot 集成 Redisson 實現(xiàn)分布式鎖

    ????????Redisson 是一種基于 Redis 的 Java 駐留集群的分布式對象和服務(wù)庫,可以為我們提供豐富的分布式鎖和線程安全集合的實現(xiàn)。在 Spring Boot 應(yīng)用程序中使用 Redisson 可以方便地實現(xiàn)分布式應(yīng)用程序的某些方面,例如分布式鎖、分布式集合、分布式事件發(fā)布和訂閱等。本篇

    2024年02月08日
    瀏覽(24)
  • Redis分布式鎖及Redisson的實現(xiàn)原理

    Redis分布式鎖及Redisson的實現(xiàn)原理

    Redis分布式鎖 在討論分布式鎖之前我們回顧一下一些單機鎖,比如synchronized、Lock 等 鎖的基本特性: 1.互斥性:同一時刻只能有一個節(jié)點訪問共享資源,比如一個代碼塊,或者同一個訂單同一時刻只能有一個線程去支付等。 2.可重入性: 允許一個已經(jīng)獲得鎖的線程,在沒有釋

    2024年02月06日
    瀏覽(23)
  • 源碼篇--Redisson 分布式鎖lock的實現(xiàn)

    我們知道Redis 緩存可以使用setNx來作為分布式鎖,但是我們直接使用setNx 需要考慮鎖過期的問題;此時我們可以使用Redisson 的lock 來實現(xiàn)分布式鎖,那么lock 內(nèi)部幫我們做了哪些工作呢。 RedisConfig.java lock.lock() 阻塞獲取 redis 鎖,獲取到鎖之后繼續(xù)向下執(zhí)行業(yè)務(wù)邏輯; lockInterr

    2024年01月25日
    瀏覽(33)
  • 在Java項目中使用redisson實現(xiàn)分布式鎖

    在Java項目中使用Redission自定義注解實現(xiàn)分布式鎖: 添加Redission依賴項:在項目的pom.xml中添加Redission依賴項: 創(chuàng)建自定義注解:創(chuàng)建一個自定義注解來標記需要使用分布式鎖的方法。例如,創(chuàng)建一個名為 @DistributedLock 的注解: 創(chuàng)建注解切面:創(chuàng)建一個切面類,通過AOP將注解

    2024年02月16日
    瀏覽(25)
  • spring boot 實現(xiàn)Redisson分布式鎖及其讀寫鎖

    分布式鎖,就是控制分布式系統(tǒng)中不同進程共同訪問同一共享資源的一種鎖的實現(xiàn)。 1、引入依賴 2、配置文件 3、配置類 4、測試代碼 5、理解 一、時間設(shè)置 默認 lock() 小結(jié) lock.lock (); (1)默認指定鎖時間為30s(看門狗時間) (2)鎖的自動續(xù)期:若是業(yè)務(wù)超長,運行期間自

    2024年02月12日
    瀏覽(20)
  • redis實戰(zhàn)-redis實現(xiàn)分布式鎖&redisson快速入門

    redis實戰(zhàn)-redis實現(xiàn)分布式鎖&redisson快速入門

    前言 集群環(huán)境下的并發(fā)問題 ?分布式鎖 定義 需要滿足的條件 常見的分布式鎖 redis實現(xiàn)分布式鎖 核心思路 代碼實現(xiàn) 誤刪情況 邏輯說明 解決方案 代碼實現(xiàn) 更為極端的誤刪情況 Lua腳本解決原子性問題 分布式鎖-redission redisson的概念 快速入門 總結(jié) 在前面我們已經(jīng)實現(xiàn)了單機

    2024年02月09日
    瀏覽(26)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包