广告
返回顶部
首页 > 资讯 > 精选 >Redis分布式锁实现的方法是什么
  • 609
分享到

Redis分布式锁实现的方法是什么

2023-07-05 20:07:00 609人浏览 泡泡鱼
摘要

本篇内容主要讲解“Redis分布式锁实现的方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Redis分布式锁实现的方法是什么”吧!一、分布式锁是什么分布式锁是 满足分布式系统或集群模式下

本篇内容主要讲解“Redis分布式实现的方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习Redis分布式锁实现的方法是什么”吧!

    一、分布式锁是什么

    分布式锁是 满足分布式系统集群模式下多进程可见并且互斥的锁。

    基于Redis实现分布式锁:

    1、获取锁

    • 互斥:确保只能有一个线程获取锁;

    • 非阻塞:尝试获取锁,成功返回true,失败返回false;

    添加锁过期时间,避免服务宕机引起死锁。

    SET lock thread1 NX EX 10

    2、释放锁

    • 手动释放;DEL key1

    • 超时释放,获取锁时添加一个超时锁;

    二、代码实例

    package com.guor.utils;import org.springframework.data.redis.core.StringRedisTemplate;import java.util.concurrent.TimeUnit;public class RedisLock implements ILock{    private String name;    private StringRedisTemplate stringRedisTemplate;    public RedisLock(String name, StringRedisTemplate stringRedisTemplate) {        this.name = name;        this.stringRedisTemplate = stringRedisTemplate;    }    private static final String KEY_PREFIX = "lock:";    @Override    public boolean tryLock(long timeout) {        // 获取线程唯一标识        long threadId = Thread.currentThread().getId();        // 获取锁        Boolean success = stringRedisTemplate.opsForValue()                .setIfAbsent(KEY_PREFIX + name, threadId+"", timeout, TimeUnit.SECONDS);        // 防止拆箱的空指针异常        return Boolean.TRUE.equals(success);    }    @Override    public void unlock() {        stringRedisTemplate.delete(KEY_PREFIX + name);    }}

    上面代码存在锁误删问题:

    1. 如果线程1获取锁,但线程1发生了阻塞,导致Redis超时释放锁;

    2. 此时,线程2尝试获取锁,成功,并执行业务;

    3. 此时,线程1重新开始执行任务,并执行完毕,执行释放锁(即删除锁);

    4. 但是,线程1删除的锁,和线程2的锁是同一把锁,这就是分布式锁误删问题

    在释放锁时,释放线程自己的分布式锁,就可以解决这个问题。

    package com.guor.utils;import cn.hutool.core.lang.UUID;import org.springframework.data.redis.core.StringRedisTemplate;import java.util.concurrent.TimeUnit;public class RedisLock implements ILock{    private String name;    private StringRedisTemplate stringRedisTemplate;    public RedisLock(String name, StringRedisTemplate stringRedisTemplate) {        this.name = name;        this.stringRedisTemplate = stringRedisTemplate;    }    private static final String KEY_PREFIX = "lock:";    private static final String UUID_PREFIX = UUID.randomUUID().toString(true) + "-";    @Override    public boolean tryLock(long timeout) {        // 获取线程唯一标识        String threadId = UUID_PREFIX + Thread.currentThread().getId();        // 获取锁        Boolean success = stringRedisTemplate.opsForValue()                .setIfAbsent(KEY_PREFIX + name, threadId, timeout, TimeUnit.SECONDS);        // 防止拆箱的空指针异常        return Boolean.TRUE.equals(success);    }    @Override    public void unlock() {        // 获取线程唯一标识        String threadId = UUID_PREFIX + Thread.currentThread().getId();        // 获取锁中的标识        String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);        // 判断标示是否一致        if(threadId.equals(id)) {            // 释放锁            stringRedisTemplate.delete(KEY_PREFIX + name);        }    }}

    三、基于SETNX实现的分布式锁存在下面几个问题

    1、不可重入

    同一个线程无法多次获取同一把锁。

    2、不可重试

    获取锁只尝试一次就返回false,没有重试机制。

    3、超时释放

    锁的超时释放虽然可以避免死锁,但如果业务执行耗时较长,也会导致锁释放,存在安全隐患。

    4、主从一致性

    如果Redis是集群部署的,主从同步存在延迟,当主机宕机时,此时会选一个从作为主机,但是此时的从没有锁标识,此时,其它线程可能会获取到锁,导致安全问题。

    四、Redisson实现分布式锁

    Redisson是一个在Redis的基础上实现的Java驻内存数据网格。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务,其中包含各种分布式锁的实现。

    1、pom

    <!--redisson--><dependency>    <groupId>org.redisson</groupId>    <artifactId>redisson</artifactId>    <version>3.13.6</version></dependency>

    2、配置类

    package com.guor.config;import org.redisson.Redisson;import org.redisson.api.RedissonClient;import org.redisson.config.Config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class RedissonConfig {    @Bean    public RedissonClient redissonClient(){        // 配置        Config config = new Config();                config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("123456");        // 创建RedissonClient对象        return Redisson.create(config);    }}

    3、测试

    package com.guor;import lombok.extern.slf4j.Slf4j;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Test;import org.redisson.api.RLock;import org.redisson.api.RedissonClient;import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;import java.util.concurrent.TimeUnit;@Slf4j@SpringBootTestclass RedissonTest {    @Resource    private RedissonClient redissonClient;    private RLock lock;    @BeforeEach    void setUp() {    // 获取指定名称的锁        lock = redissonClient.getLock("nezha");    }    @Test    void test() throws InterruptedException {        // 尝试获取锁        boolean isLock = lock.tryLock(1L, TimeUnit.SECONDS);        if (!isLock) {            log.error("获取锁失败");            return;        }        try {            log.info("哪吒最帅,哈哈哈");        } finally {            // 释放锁            lock.unlock();        }    }}

    五、探索tryLock源码

    1、tryLock源码

    尝试获取锁
    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 = this.tryAcquire(waitTime, leaseTime, unit, threadId);if (ttl == null) {return true;} else {// 剩余等待时间 = 最大等待时间 - 获取锁失败消耗的时间time -= System.currentTimeMillis() - current;if (time <= 0L) {// 获取锁失败this.acquireFailed(waitTime, unit, threadId);return false;} else {// 再次尝试获取锁current = System.currentTimeMillis();// subscribe订阅其它释放锁的信号RFuture<RedissonLockEntry> subscribeFuture = this.subscribe(threadId);// 当Future在等待指定时间time内完成时,返回trueif (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {if (!subscribeFuture.cancel(false)) {subscribeFuture.onComplete((res, e) -> {if (e == null) {// 取消订阅this.unsubscribe(subscribeFuture, threadId);}});}this.acquireFailed(waitTime, unit, threadId);return false;// 获取锁失败} else {try {// 剩余等待时间 = 剩余等待时间 - 获取锁失败消耗的时间time -= System.currentTimeMillis() - current;if (time <= 0L) {this.acquireFailed(waitTime, unit, threadId);boolean var20 = false;return var20;} else {boolean var16;do {long currentTime = System.currentTimeMillis();// 重试获取锁ttl = this.tryAcquire(waitTime, leaseTime, unit, threadId);if (ttl == null) {var16 = true;return var16;}// 再次失败了,再看一下剩余时间time -= System.currentTimeMillis() - currentTime;if (time <= 0L) {this.acquireFailed(waitTime, unit, threadId);var16 = false;return var16;}// 再重试获取锁currentTime = System.currentTimeMillis();if (ttl >= 0L && ttl < time) {// 通过信号量的方式尝试获取信号,如果等待时间内,依然没有结果,会返回false((RedissonLockEntry)subscribeFuture.getNow()).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);} else {((RedissonLockEntry)subscribeFuture.getNow()).getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);}time -= System.currentTimeMillis() - currentTime;} while(time > 0L);this.acquireFailed(waitTime, unit, threadId);var16 = false;return var16;}} finally {this.unsubscribe(subscribeFuture, threadId);}}}}}

    2、重置锁的有效期

    private void scheduleExpirationRenewal(long threadId) {RedissonLock.ExpirationEntry entry = new RedissonLock.ExpirationEntry();// this.getEntryName():锁的名字,一个锁对应一个entry// putIfAbsent:如果不存在,将锁和entry放到map里RedissonLock.ExpirationEntry oldEntry = (RedissonLock.ExpirationEntry)EXPIRATION_RENEWAL_MAP.putIfAbsent(this.getEntryName(), entry);if (oldEntry != null) {// 同一个线程多次获取锁,相当于重入oldEntry.addThreadId(threadId);} else {// 如果是第一次entry.addThreadId(threadId);// 更新有效期this.renewExpiration();}}

    更新有效期,递归调用更新有效期,永不过期

    private void renewExpiration() {// 从map中得到当前锁的entryRedissonLock.ExpirationEntry ee = (RedissonLock.ExpirationEntry)EXPIRATION_RENEWAL_MAP.get(this.getEntryName());if (ee != null) {// 开启延时任务Timeout task = this.commandExecutor.getConnectionManager().newTimeout(new TimerTask() {public void run(Timeout timeout) throws Exception {RedissonLock.ExpirationEntry ent = (RedissonLock.ExpirationEntry)RedissonLock.EXPIRATION_RENEWAL_MAP.get(RedissonLock.this.getEntryName());if (ent != null) {// 取出线程idLong threadId = ent.getFirstThreadId();if (threadId != null) {// 刷新有效期RFuture<Boolean> future = RedissonLock.this.renewExpirationAsync(threadId);future.onComplete((res, e) -> {if (e != null) {RedissonLock.log.error("Can't update lock " + RedissonLock.this.getName() + " expiration", e);} else {if (res) {// 递归调用更新有效期,永不过期RedissonLock.this.renewExpiration();}}});}}}}, this.internalLockLeaseTime / 3L, TimeUnit.MILLISECONDS);// 10See.setTimeout(task);}}
    更新有效期
    protected RFuture<Boolean> renewExpirationAsync(long threadId) {return this.evalWriteAsync(this.getName(), LonGCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, // 判断当前线程的锁是否是当前线程"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then // 更新有效期redis.call('pexpire', KEYS[1], ARGV[1]); return 1; end; return 0;", Collections.singletonList(this.getName()), this.internalLockLeaseTime, this.getLockName(threadId));}

    3、调用lua脚本

    <T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {// 锁释放时间this.internalLockLeaseTime = unit.toMillis(leaseTime);return this.evalWriteAsync(this.getName(), LongCodec.INSTANCE, command, // 判断锁成功"if (redis.call('exists', KEYS[1]) == 0) thenredis.call('hincrby', KEYS[1], ARGV[2], 1); // 如果不存在,记录锁标识,次数+1redis.call('pexpire', KEYS[1], ARGV[1]); // 设置锁有效期return nil; // 相当于Java的nullend; if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then redis.call('hincrby', KEYS[1], ARGV[2], 1); // 如果存在,判断锁标识是否是自己的,次数+1redis.call('pexpire', KEYS[1], ARGV[1]); // 设置锁有效期return nil; end; // 判断锁失败,pttl:指定锁剩余有效期,单位毫秒,KEYS[1]:锁的名称return redis.call('pttl', KEYS[1]);", Collections.singletonList(this.getName()), this.internalLockLeaseTime, this.getLockName(threadId));}

    六、释放锁unlock源码

    1、取消更新任务

    public RFuture<Void> unlockAsync(long threadId) {RPromise<Void> result = new RedissonPromise();RFuture<Boolean> future = this.unlockInnerAsync(threadId);future.onComplete((opStatus, e) -> {// 取消更新任务this.cancelExpirationRenewal(threadId);if (e != null) {result.tryFailure(e);} else if (opStatus == null) {IllegalMonitorStateException cause = new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: " + this.id + " thread-id: " + threadId);result.tryFailure(cause);} else {result.trySuccess((Object)null);}});return result;}

    2、删除定时任务

    void cancelExpirationRenewal(Long threadId) {// 从map中取出当前锁的定时任务entryRedissonLock.ExpirationEntry task = (RedissonLock.ExpirationEntry)EXPIRATION_RENEWAL_MAP.get(this.getEntryName());if (task != null) {if (threadId != null) {task.removeThreadId(threadId);}// 删除定时任务if (threadId == null || task.hasNoThreads()) {Timeout timeout = task.getTimeout();if (timeout != null) {timeout.cancel();}EXPIRATION_RENEWAL_MAP.remove(this.getEntryName());}}}

    到此,相信大家对“Redis分布式锁实现的方法是什么”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

    --结束END--

    本文标题: Redis分布式锁实现的方法是什么

    本文链接: https://www.lsjlt.com/news/355019.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    本篇文章演示代码以及资料文档资料下载

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档
    猜你喜欢
    • Redis实现分布式锁的方法是什么
      本篇内容介绍了“Redis实现分布式锁的方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!在一个分布...
      99+
      2022-10-19
    • Redis分布式锁实现的方法是什么
      本篇内容主要讲解“Redis分布式锁实现的方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Redis分布式锁实现的方法是什么”吧!一、分布式锁是什么分布式锁是 满足分布式系统或集群模式下...
      99+
      2023-07-05
    • 分布式锁redis实现方式是什么
      分布式锁的Redis实现方式有两种:基于SETNX命令和基于RedLock算法。1. 基于SETNX命令:使用Redis的SETNX...
      99+
      2023-09-12
      redis
    • Redis实现分布式锁的五种方法是什么
      本文小编为大家详细介绍“Redis实现分布式锁的五种方法是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Redis实现分布式锁的五种方法是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起...
      99+
      2022-10-19
    • redis实现分布式重入锁的方法是什么
      这篇文章主要为大家分析了redis实现分布式重入锁的方法是什么的相关知识点,内容详细易懂,操作细节合理,具有一定参考价值。如果感兴趣的话,不妨跟着跟随小编一起来看看,下面跟着小编一起深入学习“redis实现...
      99+
      2022-10-19
    • redis实现分布式锁的方法
      本篇文章展示了redis实现分布式锁的方法具体操作,代码简明扼要容易理解,绝对能让你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。分布式锁其实可以理解为:控制分布式系统有序的去对共享资源进行操作,通过互...
      99+
      2022-10-18
    • redis中分布式锁的实现方法
      小编给大家分享一下redis中分布式锁的实现方法,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!redis分布式锁:1、实现原理利...
      99+
      2022-10-18
    • Redis分布式锁的实现方式
      目录一、分布式锁是什么1、获取锁2、释放锁二、代码实例上面代码存在锁误删问题:三、基于SETNX实现的分布式锁存在下面几个问题1、不可重入2、不可重试3、超时释放4、主从一致性四、Redisson实现分布式锁1、pom2...
      99+
      2023-04-03
      Java Redis分布式锁实现方式 实现Redis分布式锁 Redis分布式锁实现
    • Redis实现分布式锁方法详细
      目录1. 单机数据一致性2. 分布式数据一致性3. Redis实现分布式锁3.1 方式一3.2 方式二(改进方式一)3.3 方式三(改进方式二)3.4 方式四(改进方式三)3.5 方...
      99+
      2022-11-12
    • 使用Redis实现分布式锁的方法
      目录Redis 中的分布式锁如何使用分布式锁的使用场景使用 Redis 来实现分布式锁使用 set key value px milliseconds nx 实现SETNX+Lua 实现使用 Redlock 实现分布式锁...
      99+
      2022-06-16
      Redis分布式锁
    • Redis实现分布式锁的方法示例
      之前我们使用的定时任务都是只部署在了单台机器上,为了解决单点的问题,为了保证一个任务,只被一台机器执行,就需要考虑锁的问题,于是就花时间研究了这个问题。到底怎样实现一个分布式锁呢? 锁的本质就是互斥,保证任...
      99+
      2022-06-04
      分布式 示例 方法
    • mysql分布式锁实现的方法是什么
      MySQL本身并没有提供分布式锁的实现方法,但可以借助MySQL的特性和其他技术来实现分布式锁。以下是几种常见的实现方法:1. 基于...
      99+
      2023-10-09
      mysql
    • redis实现分布式锁的原理是什么
      这期内容当中小编将会给大家带来有关redis实现分布式锁的原理,以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。分布式锁,是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常...
      99+
      2022-10-18
    • redis分布式锁的实现原理是什么
      这篇文章主要讲解了“redis分布式锁的实现原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“redis分布式锁的实现原理是什么”吧!借助于redis...
      99+
      2022-10-18
    • Redis实现分布式锁的方法有哪些
      今天小编给大家分享一下Redis实现分布式锁的方法有哪些的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1. 单机数据一致性单...
      99+
      2023-07-02
    • Redis分布式锁的正确实现方式
      Redis分布式锁的正确实现方式?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。可靠性首先,为了确保分布式锁可用,我们至少要确保...
      99+
      2022-10-18
    • redis分布式锁的实现
      一、使用分布式锁要满足的几个条件:1、系统是一个分布式系统(关键是分布式,单机的可以使用ReentrantLock或者synchronized代码块来实现)2、共享资源(各个系统访问同一个资源,资源的载体可...
      99+
      2022-10-18
    • Redis实现分布式锁
      单体锁存在的问题 在单体应用中,如果我们对共享数据不进行加锁操作,多线程操作共享数据时会出现数据一致性问题。 (下述实例是一个简单的下单问题:从redis中获取库存,检查库存是否够,>0才允许下单) 我们的解决办法通常是加锁。如下加单体锁...
      99+
      2023-08-16
      分布式 java jvm
    • Redis——》实现分布式锁
      推荐链接:     总结——》【Java】     总结——》【Mysql】     总结——》【Redis】     总结——》【Kafka】     总结——》【Spring】     总结—...
      99+
      2023-09-03
      redis 分布式 过期 lua
    • 分布式锁的原理及Redis怎么实现分布式锁
      这篇文章主要介绍“分布式锁的原理及Redis怎么实现分布式锁”,在日常操作中,相信很多人在分布式锁的原理及Redis怎么实现分布式锁问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解...
      99+
      2023-02-02
      redis
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作