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

Redis Redisson lock和tryLock的原理分析

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

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();
    }

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

    /**
     * 帶超時時間的鎖
     *
     * @param lockKey   key
     * @param leaseTime 上鎖后自動釋放鎖時間
     * @param unit      時間單位
     */
    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  最多等待時間
     * @param leaseTime 上鎖后自動釋放鎖時間
     * @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  最多等待時間
     * @param leaseTime 上鎖后自動釋放鎖時間
     * @param unit      時間單位
     * @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.時機(jī)

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

lock拿不到鎖會一直等待。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,競爭鎖時返回鎖的過期時間
        Long ttl = tryAcquire(-1, leaseTime, unit, threadId);
        // lock acquired
        if (ttl == null) {
            return;
        }

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

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

第一個if:

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

第二個if:

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

最后返回:

  • 這個最后返回不是說最后結(jié)果返回,是代表以上兩個if都沒有進(jìn)入,則代表處于競爭鎖的情況,后續(xù)返回競爭鎖的過期時間。

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

總結(jié)

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

相關(guān)文章

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

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

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

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

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

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

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

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

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

    詳解redis中的鎖以及使用場景

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

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

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

    比較幾種Redis集群方案

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

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

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

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

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

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

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

最新評論

武清区| 比如县| 江永县| 上林县| 松滋市| 康平县| 北流市| 贵溪市| 壤塘县| 贵德县| 江川县| 渭南市| 家居| 桐庐县| 修文县| 巴青县| 裕民县| 天水市| 德清县| 青田县| 社旗县| 马龙县| 儋州市| 曲周县| 安塞县| 靖西县| 安康市| 梧州市| 义马市| 双流县| 烟台市| 洮南市| 车险| 旬邑县| 石门县| 绥棱县| 出国| 若尔盖县| 湘西| 靖州| 长寿区|