亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Redis如何實現可重入鎖的設計

發布時間:2021-03-18 11:44:43 來源:億速云 閱讀:388 作者:小新 欄目:關系型數據庫

這篇文章主要介紹Redis如何實現可重入鎖的設計,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

但是仍然有些場景是不滿?的,例如? 個?法獲取到鎖之后,可能在?法內調這個?法此時就獲取不到鎖了。這個時候我們就需要把鎖改進成可 重?鎖了。 重?鎖,指的是以線程為單位,當?個線程獲取對象鎖之后,這個線程可以再次獲取本對象上的鎖,?其 他的線程是不可以的。可重?鎖的意義在于防?死鎖。 實現原理是通過為每個鎖關聯?個請求計數器和?個占有它的線程。當計數為 0 時,認為鎖是未被占有 的;線程請求?個未被占有的鎖時,JVM 將記錄鎖的占有者,并且將請求計數器置為 1 。 如果同?個線程再次請求這個鎖,計數將遞增;每次占?線程退出同步塊,計數器值將遞減。直到計數器 為 0, 鎖被釋放。 關于?類和?類的鎖的重?:?類覆寫了?類的 synchonized ?法,然后調??類中的?法,此時如果沒有重?的鎖,那么這段代碼將產?死鎖。

代碼演示

不可重?

  • 不可重?鎖
    Redis如何實現可重入鎖的設計

  • 使用不可重入鎖Redis如何實現可重入鎖的設計
    當前線程執? call() ?法?先獲取 lock,接下來執? inc() ?法就?法執? inc() 中的邏輯,必須先釋放鎖。該例很好的說明了不可重?鎖。

可重入鎖

  • 鎖實現
    Redis如何實現可重入鎖的設計

  • 鎖使用Redis如何實現可重入鎖的設計

可重?意味著線程可進?它已經擁有的鎖的同步代碼塊。

設計兩個線程調? call() ?法,第?個線程調? call() ?法獲取鎖,進? lock() ?法,由于初始 lockedBy 是 null,所以不會進? while ?掛起當前線程,?是增量 lockedCount 并記錄 lockBy 為第 ?個線程。

接著第?個線程進? inc() ?法,由于同?進程,所以不會進? while ?掛起,接著增量 lockedCount,當第?個線程嘗試 lock,由于 isLocked=true, 所以他不會獲取該鎖,直到第?個線程調?兩次 unlock() 將 lockCount 遞減為 0,才將標記為 isLocked 設置為 false。

設計思路

假設鎖的key為“lock”,hashKey是當前線程的id:“threadId”,鎖自動釋放時間假設為20。

獲取鎖

判斷lock是否存在 EXISTS lock

  • 不存在,則自己獲取鎖,記錄重入層數為1.

  • 存在,說明有人獲取鎖了,繼續判斷是不是自己的鎖,即判斷當前線程id作為hashKey是否存在:HEXISTS lock threadId

    • 不存在,說明鎖已經有了,且不是自己獲取的,鎖獲取失敗.

    • 存在,說明是自己獲取的鎖,重入次數+1: HINCRBY lock threadId 1 ,最后更新鎖自動釋放時間, EXPIRE lock 20
      Redis如何實現可重入鎖的設計

釋放鎖

判斷當前線程id作為hashKey是否存在: HEXISTS lock threadId

  • 不存在,說明鎖已失效

  • 存在,說明鎖還在,重入次數減1: HINCRBY lock threadId -1 ,

    • 獲取新的重入次數,判斷重入次數是否為0,為0說明鎖全部釋放,刪除key: DEL lock

因此,存儲在鎖中的信息就必須包含:key、線程標識、重入次數。不能再使用簡單的 key-value 結構, 這里推薦使用 hash 結構。而且要讓所有指令都在同一個線程中操作,那么使用 lua 腳本。

lua 腳本

lock.lua

local key = KEYS[1]; -- 第1個參數,鎖的keylocal threadId = ARGV[1]; -- 第2個參數,線程唯一標識local releaseTime = ARGV[2]; -- 第3個參數,鎖的自動釋放時間if(redis.call('exists', key) == 0) then -- 判斷鎖是否已存在
    redis.call('hset', key, threadId, '1'); -- 不存在, 則獲取鎖
    redis.call('expire', key, releaseTime); -- 設置有效期
    return 1; -- 返回結果end;if(redis.call('hexists', key, threadId) == 1) then -- 鎖已經存在,判斷threadId是否是自己    
    redis.call('hincrby', key, threadId, '1'); -- 如果是自己,則重入次數+1
    redis.call('expire', key, releaseTime); -- 設置有效期
    return 1; -- 返回結果end;return 0; -- 代碼走到這里,說明獲取鎖的不是自己,獲取鎖失敗

unlock.lua

-- 鎖的 keylocal key = KEYS[1];-- 線程唯一標識local threadId = ARGV[1];-- 判斷當前鎖是否還是被自己持有if (redis.call('hexists', key, threadId) == 0) then--     如果已經不是自己,則直接返回
    return nil;end;-- 是自己的鎖,則重入次數減一local count = redis.call('hincrby', key, threadId, -1);-- 判斷重入次數是否已為0if (count == 0) then--     等于 0,說明可以釋放鎖,直接刪除
    redis.call('del', key);
    return nil;end;

在項目中集成

編寫 RedisLock 類

@Getter@Setterpublic class RedisLock {

    private RedisTemplate redisTemplate;
    private DefaultRedisScript<Long> lockScript;
    private DefaultRedisScript<Object> unlockScript;

    public RedisLock(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
        // 加載釋放鎖的腳本
        this.lockScript = new DefaultRedisScript<>();
        this.lockScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lock.lua")));
        this.lockScript.setResultType(Long.class);
        // 加載釋放鎖的腳本
        this.unlockScript = new DefaultRedisScript<>();
        this.unlockScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("unlock.lua")));
    }

    /**
     * 獲取鎖
     * @param lockName 鎖名稱
     * @param releaseTime 超時時間(單位:秒)
     * @return key 解鎖標識
     */
    public String tryLock(String lockName, long releaseTime) {
        // 存入的線程信息的前綴,防止與其它JVM中線程信息沖突
        String key = UUID.randomUUID().toString();

        // 執行腳本
        Long result = (Long)redisTemplate.execute(
                lockScript,
                Collections.singletonList(lockName),
                key + Thread.currentThread().getId(), releaseTime);

        // 判斷結果
        if(result != null && result.intValue() == 1) {
            return key;
        }else {
            return null;
        }
    }
    /**
     * 釋放鎖
     * @param lockName 鎖名稱
     * @param key 解鎖標識
     */
    public void unlock(String lockName, String key) {
        // 執行腳本
        redisTemplate.execute(
                unlockScript,
                Collections.singletonList(lockName),
                key + Thread.currentThread().getId(), null);
    }}

以上是“Redis如何實現可重入鎖的設計”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

嘉兴市| 蚌埠市| 饶阳县| 北票市| 新邵县| 蒙城县| 元江| 德钦县| 定南县| 朝阳县| 重庆市| 天柱县| 黄浦区| 高密市| 阜康市| 马鞍山市| 阿图什市| 诏安县| 永新县| 榆林市| 恭城| 山东| 库尔勒市| 内黄县| 贡嘎县| 庆阳市| 治县。| 南汇区| 融水| 湘潭市| 清丰县| 九台市| 云阳县| 敦化市| 汾西县| 雷山县| 漳浦县| 工布江达县| 尖扎县| 赤壁市| 称多县|