最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Redis Redisson lock和tryLock的原理分析

 更新時(shí)間:2024年04月23日 09:39:34   作者:奧特迦  
這篇文章主要介紹了Redis Redisson lock和tryLock的原理分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

Redisson 分布式鎖原理

在這里插入圖片描述

1. 工具類

package com.meta.mall.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * redisson 分布式工具類
 *
 * @author gaoyang
 * @date 2022-05-14 08:58
 */
@Slf4j
@Component
public class RedissonUtils {

    @Resource
    private RedissonClient redissonClient;

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

    /**
     * 帶過(guò)期時(shí)間的鎖
     *
     * @param lockKey   key
     * @param leaseTime 上鎖后自動(dòng)釋放鎖時(shí)間
     */
    public void lock(String lockKey, long leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(leaseTime, TimeUnit.SECONDS);
    }

    /**
     * 帶超時(shí)時(shí)間的鎖
     *
     * @param lockKey   key
     * @param leaseTime 上鎖后自動(dòng)釋放鎖時(shí)間
     * @param unit      時(shí)間單位
     */
    public void lock(String lockKey, long leaseTime, TimeUnit unit) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(leaseTime, unit);
    }

    /**
     * 嘗試獲取鎖
     *
     * @param lockKey key
     * @return
     */
    public boolean tryLock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        return lock.tryLock();
    }

    /**
     * 嘗試獲取鎖
     *
     * @param lockKey   key
     * @param waitTime  最多等待時(shí)間
     * @param leaseTime 上鎖后自動(dòng)釋放鎖時(shí)間
     * @return boolean
     */
    public boolean tryLock(String lockKey, long waitTime, long leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            log.error("RedissonUtils - tryLock異常", e);
        }

        return false;
    }

    /**
     * 嘗試獲取鎖
     *
     * @param lockKey   key
     * @param waitTime  最多等待時(shí)間
     * @param leaseTime 上鎖后自動(dòng)釋放鎖時(shí)間
     * @param unit      時(shí)間單位
     * @return boolean
     */
    public boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) {
            log.error("RedissonUtils - tryLock異常", e);
        }

        return false;
    }

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

    /**
     * 是否存在鎖
     *
     * @param lockKey key
     * @return
     */
    public boolean isLocked(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        return lock.isLocked();
    }
}

2. lock和tryLock的區(qū)別

1.返回值

  • lock 是 void;
  • tryLock 是 boolean。

2.時(shí)機(jī)

  • lock 一直等鎖釋放;
  • tryLock 獲取到鎖返回true,獲取不到鎖并直接返回false。

lock拿不到鎖會(huì)一直等待。tryLock是去嘗試,拿不到就返回false,拿到返回true。

tryLock是可以被打斷的,被中斷的,lock是不可以。

3. 源碼分析

3.1 lock

private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
		// 獲取當(dāng)前線程 ID
        long threadId = Thread.currentThread().getId();
		// 獲取鎖,正常獲取鎖則ttl為null,競(jìng)爭(zhēng)鎖時(shí)返回鎖的過(guò)期時(shí)間
        Long ttl = tryAcquire(-1, leaseTime, unit, threadId);
        // lock acquired
        if (ttl == null) {
            return;
        }

		// 訂閱鎖釋放事件
		// 如果當(dāng)前線程通過(guò) Redis 的 channel 訂閱鎖的釋放事件獲取得知已經(jīng)被釋放,則會(huì)發(fā)消息通知待等待的線程進(jìn)行競(jìng)爭(zhēng)
        RFuture<RedissonLockEntry> future = subscribe(threadId);
        if (interruptibly) {
            commandExecutor.syncSubscriptionInterrupted(future);
        } else {
            commandExecutor.syncSubscription(future);
        }

        try {
            while (true) {
                // 循環(huán)重試獲取鎖,直至重新獲取鎖成功才跳出循環(huán)
                // 此種做法阻塞進(jìn)程,一直處于等待鎖手動(dòng)釋放或者超時(shí)才繼續(xù)線程
                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));
    }
    <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]: 為設(shè)置的過(guò)期時(shí)間

第一個(gè)if:

  • 判斷是否存在設(shè)置lock的key是否存在,不存在則利用redis的hash結(jié)構(gòu)設(shè)置一個(gè)hash,值為1,并設(shè)置過(guò)期時(shí)間,后續(xù)返回鎖。

第二個(gè)if:

  • 判斷是否存在設(shè)置lock的key是否存在,存在此線程的hash,則為這個(gè)鎖的重入次數(shù)加1(將hash值+1),并重新設(shè)置過(guò)期時(shí)間,后續(xù)返回鎖。

最后返回:

  • 這個(gè)最后返回不是說(shuō)最后結(jié)果返回,是代表以上兩個(gè)if都沒(méi)有進(jìn)入,則代表處于競(jìng)爭(zhēng)鎖的情況,后續(xù)返回競(jìng)爭(zhēng)鎖的過(guò)期時(shí)間。

3.2 tryLock

tryLock具有返回值,true或者false,表示是否成功獲取鎖。

tryLock前期獲取鎖邏輯基本與lock一致,主要是后續(xù)獲取鎖失敗的處理邏輯與lock不一致。

    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;
        }
        
        // 獲取鎖失敗后,中途tryLock會(huì)一直判斷中間操作耗時(shí)是否已經(jīng)消耗鎖的過(guò)期時(shí)間,如果消耗完則返回false
        time -= System.currentTimeMillis() - current;
        if (time <= 0) {
            acquireFailed(waitTime, unit, threadId);
            return false;
        }
        
        current = System.currentTimeMillis();
        // 訂閱鎖釋放事件
        // 如果當(dāng)前線程通過(guò) Redis 的 channel 訂閱鎖的釋放事件獲取得知已經(jīng)被釋放,則會(huì)發(fā)消息通知待等待的線程進(jìn)行競(jìng)爭(zhēng).
        RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);
        // 將訂閱阻塞,阻塞時(shí)間設(shè)置為我們調(diào)用tryLock設(shè)置的最大等待時(shí)間,超過(guò)時(shí)間則返回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;
            }
        	
        	// 循環(huán)獲取鎖,但由于上面有最大等待時(shí)間限制,基本會(huì)在上面返回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));
    }

應(yīng)盡量使用tryLock,且攜帶參數(shù),因?yàn)榭稍O(shè)置最大等待時(shí)間以及可及時(shí)獲取加鎖返回值,后續(xù)可做一些其他加鎖失敗的業(yè)務(wù)

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 淺談我是如何用redis做實(shí)時(shí)訂閱推送的

    淺談我是如何用redis做實(shí)時(shí)訂閱推送的

    這篇文章主要介紹了淺談我是如何用redis做實(shí)時(shí)訂閱推送的,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • 基于Redis實(shí)現(xiàn)分布式單號(hào)及分布式ID(自定義規(guī)則生成)

    基于Redis實(shí)現(xiàn)分布式單號(hào)及分布式ID(自定義規(guī)則生成)

    一些業(yè)務(wù)背景下,業(yè)務(wù)要求單號(hào)需要有區(qū)分不同的前綴,那么在分布式的架構(gòu)下如何自定義單號(hào)而且還能保證唯一呢?本文就來(lái)詳細(xì)的介紹一下
    2021-09-09
  • Windows環(huán)境下查看、添加、修改redis數(shù)據(jù)庫(kù)的密碼兩種方式

    Windows環(huán)境下查看、添加、修改redis數(shù)據(jù)庫(kù)的密碼兩種方式

    在Windows系統(tǒng)上設(shè)置Redis密碼的過(guò)程與Linux系統(tǒng)類似,但需注意幾個(gè)關(guān)鍵步驟以確保正確配置,這篇文章主要給大家介紹了關(guān)于Windows環(huán)境下查看、添加、修改redis數(shù)據(jù)庫(kù)的密碼兩種方式,需要的朋友可以參考下
    2024-07-07
  • 對(duì)Redis中事務(wù)的理解分析

    對(duì)Redis中事務(wù)的理解分析

    文章介紹了Redis事務(wù)的實(shí)現(xiàn)方式,通過(guò)MULTI、EXEC、WATCH等命令實(shí)現(xiàn)原子性、一致性、隔離性,部分持久化模式下具備持久性,與傳統(tǒng)數(shù)據(jù)庫(kù)ACID特性類似
    2025-08-08
  • 詳解redis中的鎖以及使用場(chǎng)景

    詳解redis中的鎖以及使用場(chǎng)景

    這篇文章主要介紹了詳解redis中的鎖以及使用場(chǎng)景,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • redis由于目標(biāo)計(jì)算機(jī)積極拒絕,無(wú)法連接的解決

    redis由于目標(biāo)計(jì)算機(jī)積極拒絕,無(wú)法連接的解決

    這篇文章主要介紹了redis由于目標(biāo)計(jì)算機(jī)積極拒絕,無(wú)法連接的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • 比較幾種Redis集群方案

    比較幾種Redis集群方案

    Redis高可用集群是一個(gè)由多個(gè)主從節(jié)點(diǎn)群組成的分布式服務(wù)器群,它具有復(fù)制、高可用和分片特性。Redis集群不需要sentinel哨兵也能完成節(jié)點(diǎn)移除和故障轉(zhuǎn)移的功能,只要將每個(gè)節(jié)點(diǎn)設(shè)置成集群模式,這種集群模式?jīng)]有中心節(jié)點(diǎn),可水平擴(kuò)展,官方稱可以線性擴(kuò)展到上萬(wàn)個(gè)節(jié)點(diǎn)
    2021-06-06
  • Redis實(shí)現(xiàn)高并發(fā)計(jì)數(shù)器

    Redis實(shí)現(xiàn)高并發(fā)計(jì)數(shù)器

    這篇文章主要為大家詳細(xì)介紹了Redis實(shí)現(xiàn)高并發(fā)計(jì)數(shù)器,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-10-10
  • Redis在項(xiàng)目中的使用(JedisPool方式)

    Redis在項(xiàng)目中的使用(JedisPool方式)

    項(xiàng)目操作redis是使用的RedisTemplate方式,另外還可以完全使用JedisPool和Jedis來(lái)操作redis,本文給大家介紹Redis在項(xiàng)目中的使用,JedisPool方式,感興趣的朋友跟隨小編一起看看吧
    2021-12-12
  • redis生成全局id的實(shí)現(xiàn)步驟

    redis生成全局id的實(shí)現(xiàn)步驟

    生成全局唯一的標(biāo)識(shí)符是非常常見(jiàn)的需求,本文主要介紹了redis生成全局id的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-05-05

最新評(píng)論

县级市| 勐海县| 田林县| 沅陵县| 花垣县| 金秀| 娄烦县| 盘锦市| 鞍山市| 敖汉旗| 凉城县| 天峻县| 丘北县| 肇东市| 翁牛特旗| 祁连县| 咸宁市| 灵寿县| 巨鹿县| 颍上县| 巫山县| 琼中| 肇源县| 新化县| 清徐县| 东港市| 韶关市| 泸溪县| 安多县| 镇康县| 家居| 吉木萨尔县| 内乡县| 临夏县| 四子王旗| 潮州市| 博爱县| 察隅县| 壶关县| 宽城| 阿鲁科尔沁旗|